ソースを参照

Merge pull request #2331 from codex128/imageShaderMod

Enables images to be read and modified per pixel in shader
Ryan McDonough 8 ヶ月 前
コミット
662b4ea79f

+ 12 - 3
jme3-core/src/main/java/com/jme3/material/Material.java

@@ -50,6 +50,7 @@ import com.jme3.shader.*;
 import com.jme3.shader.bufferobject.BufferObject;
 import com.jme3.texture.Image;
 import com.jme3.texture.Texture;
+import com.jme3.texture.TextureImage;
 import com.jme3.texture.image.ColorSpace;
 import com.jme3.util.ListMap;
 import com.jme3.util.SafeArrayList;
@@ -451,7 +452,7 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
         }
         return null;
     }
-
+    
     /**
      * Returns a collection of all parameters set on this material.
      *
@@ -514,6 +515,10 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
             if (technique != null) {
                 technique.notifyParamChanged(name, type, value);
             }
+            if (type.isImageType()) {
+                // recompute sort id
+                sortingId = -1;
+            }
         }
     }
 
@@ -859,9 +864,13 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
             Uniform uniform = shader.getUniform(param.getPrefixedName());
             if (!override && uniform.isSetByCurrentMaterial()) return;
 
-            if (type.isTextureType()) {
+            if (type.isTextureType() || type.isImageType()) {
                 try {
-                    renderer.setTexture(unit.textureUnit, (Texture) param.getValue());
+                    if (type.isTextureType()) {
+                        renderer.setTexture(unit.textureUnit, (Texture) param.getValue());
+                    } else {
+                        renderer.setTextureImage(unit.textureUnit, (TextureImage) param.getValue());
+                    }
                 } catch (TextureUnitException exception) {
                     int numTexParams = unit.textureUnit + 1;
                     String message = "Too many texture parameters (" + numTexParams + ") assigned\n to " + toString();

+ 10 - 0
jme3-core/src/main/java/com/jme3/renderer/Renderer.java

@@ -42,6 +42,7 @@ import com.jme3.system.AppSettings;
 import com.jme3.texture.FrameBuffer;
 import com.jme3.texture.Image;
 import com.jme3.texture.Texture;
+import com.jme3.texture.TextureImage;
 import com.jme3.util.NativeObject;
 import java.nio.ByteBuffer;
 import java.util.EnumMap;
@@ -273,6 +274,15 @@ public interface Renderer {
      */
     public void setTexture(int unit, Texture tex)
             throws TextureUnitException;
+    
+    /**
+     * Assigns a TextureImage to the specified texture unit.
+     * 
+     * @param unit the index of the texture unit (≥0)
+     * @param tex the texture image to assign
+     * @throws TextureUnitException if the texture unit does not exist
+     */
+    public void setTextureImage(int unit, TextureImage tex) throws TextureUnitException;
 
     /**
      * Modifies the given Texture with the given Image.

+ 4 - 0
jme3-core/src/main/java/com/jme3/renderer/opengl/GL2.java

@@ -74,6 +74,10 @@ public interface GL2 extends GL {
     public static final int GL_TEXTURE_WRAP_R = 0x8072;
     public static final int GL_VERTEX_PROGRAM_POINT_SIZE = 0x8642;
     public static final int GL_UNSIGNED_INT_8_8_8_8 = 0x8035;
+    
+    public static final int GL_READ_ONLY = 35000;
+    public static final int GL_WRITE_ONLY = 35001;
+    public static final int GL_READ_WRITE = 35002;
 
     /**
      * <p><a target="_blank" href="http://docs.gl/gl3/glAlphaFunc">Reference Page</a> - <em>This function is deprecated and unavailable in the Core profile</em></p>

+ 15 - 0
jme3-core/src/main/java/com/jme3/renderer/opengl/GL4.java

@@ -100,4 +100,19 @@ public interface GL4 extends GL3 {
      * @param storageBlockBinding The index storage block binding to associate with the specified storage block.
      */
     public void glShaderStorageBlockBinding(int program, int storageBlockIndex, int storageBlockBinding);
+    
+    /**
+     * Binds a single level of a texture to an image unit for the purpose of reading
+     * and writing it from shaders.
+     * 
+     * @param unit image unit to bind to
+     * @param texture texture to bind to the image unit
+     * @param level level of the texture to bind
+     * @param layered true to bind all array elements
+     * @param layer if not layered, the layer to bind
+     * @param access access types that may be performed
+     * @param format format to use when performing formatted stores
+     */
+    public void glBindImageTexture(int unit, int texture, int level, boolean layered, int layer, int access, int format);
+    
 }

+ 15 - 1
jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java

@@ -61,6 +61,7 @@ import com.jme3.texture.Texture;
 import com.jme3.texture.Texture2D;
 import com.jme3.texture.Texture.ShadowCompareMode;
 import com.jme3.texture.Texture.WrapAxis;
+import com.jme3.texture.TextureImage;
 import com.jme3.texture.image.LastTextureState;
 import com.jme3.util.BufferUtils;
 import com.jme3.util.ListMap;
@@ -2752,7 +2753,20 @@ public final class GLRenderer implements Renderer {
             if (tex.getName() != null) glext.glObjectLabel(GL.GL_TEXTURE, tex.getImage().getId(), tex.getName());
         }
     }
-
+    
+    @Override
+    public void setTextureImage(int unit, TextureImage tex) throws TextureUnitException {
+        if (unit < 0 || unit >= RenderContext.maxTextureUnits) {
+            throw new TextureUnitException();
+        }
+        WeakReference<Image> ref = context.boundTextures[unit];
+        boolean bindRequired = tex.clearUpdateNeeded() || ref == null || ref.get() != tex.getImage().getWeakRef().get();
+        setTexture(unit, tex.getTexture());
+        if (gl4 != null && bindRequired) {
+            tex.bindImage(gl4, texUtil, unit);
+        }
+    }
+    
     @Override
     public void setUniformBufferObject(int bindingPoint, BufferObject bufferObject) {
         if (bufferObject.isUpdateNeeded()) {

+ 2 - 2
jme3-core/src/main/java/com/jme3/renderer/opengl/TextureUtil.java

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2021 jMonkeyEngine
+ * Copyright (c) 2009-2024 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -46,7 +46,7 @@ import java.util.logging.Logger;
  * 
  * @author Kirill Vainer
  */
-final class TextureUtil {
+public final class TextureUtil {
 
     private static final Logger logger = Logger.getLogger(TextureUtil.class.getName());
 

+ 18 - 3
jme3-core/src/main/java/com/jme3/shader/VarType.java

@@ -43,9 +43,10 @@ import com.jme3.texture.Texture2D;
 import com.jme3.texture.Texture3D;
 import com.jme3.texture.TextureArray;
 import com.jme3.texture.TextureCubeMap;
+import com.jme3.texture.TextureImage;
 
 public enum VarType {
-
+    
     Float("float", float.class, Float.class),
     Vector2("vec2", Vector2f.class),
     Vector3("vec3", Vector3f.class),
@@ -56,7 +57,8 @@ public enum VarType {
     Vector2Array(true, false, "vec2", Vector2f[].class),
     Vector3Array(true, false, "vec3", Vector3f[].class),
     Vector4Array(true, false, "vec4", Vector4f[].class),
-
+    
+    Int("int", int.class, Integer.class),
     Boolean("bool", Boolean.class, boolean.class),
 
     Matrix3(true, false, "mat3", Matrix3f.class),
@@ -70,12 +72,16 @@ public enum VarType {
     Texture3D(false, true, "sampler3D", Texture3D.class, Texture.class),
     TextureArray(false, true, "sampler2DArray|sampler2DArrayShadow", TextureArray.class, Texture.class),
     TextureCubeMap(false, true, "samplerCube", TextureCubeMap.class, Texture.class),
-    Int("int", int.class, Integer.class),
+    
+    Image2D(false, false, true, "image2D", TextureImage.class),
+    Image3D(false, false, true, "image3D", TextureImage.class),
+    
     UniformBufferObject(false, false, "custom", BufferObject.class),
     ShaderStorageBufferObject(false, false, "custom", BufferObject.class);
 
     private boolean usesMultiData = false;
     private boolean textureType = false;
+    private boolean imageType = false;
     private final String glslType;
     private Class<?>[] javaTypes;
 
@@ -98,6 +104,11 @@ public enum VarType {
             this.javaTypes = new Class<?>[0];
         }
     }
+    
+    VarType(boolean multiData, boolean textureType, boolean imageType, String glslType, Class<?>... javaTypes) {
+        this(multiData, textureType, glslType, javaTypes);
+        this.imageType = imageType;
+    }
 
     /**
      * Check if the passed object is of a type mapped to this VarType
@@ -126,6 +137,10 @@ public enum VarType {
     public boolean isTextureType() {
         return textureType;
     }
+    
+    public boolean isImageType() {
+        return imageType;
+    }
 
     public boolean usesMultiData() {
         return usesMultiData;

+ 7 - 0
jme3-core/src/main/java/com/jme3/system/NullRenderer.java

@@ -48,6 +48,7 @@ import com.jme3.shader.bufferobject.BufferObject;
 import com.jme3.texture.FrameBuffer;
 import com.jme3.texture.Image;
 import com.jme3.texture.Texture;
+import com.jme3.texture.TextureImage;
 
 import java.nio.ByteBuffer;
 import java.util.EnumMap;
@@ -170,6 +171,11 @@ public class NullRenderer implements Renderer {
     public void setTexture(int unit, Texture tex) throws TextureUnitException {
         // do nothing
     }
+    
+    @Override
+    public void setTextureImage(int unit, TextureImage tex) throws TextureUnitException {
+        // do nothing
+    }
 
     @Override
     public void modifyTexture(Texture tex, Image pixels, int x, int y) {
@@ -314,4 +320,5 @@ public class NullRenderer implements Renderer {
     public void setUniformBufferObject(int bindingPoint, BufferObject bufferObject) {
 
     }
+    
 }

+ 0 - 1
jme3-core/src/main/java/com/jme3/texture/Image.java

@@ -1299,7 +1299,6 @@ public class Image extends NativeObject implements Savable /*, Cloneable*/ {
         multiSamples = capsule.readInt("multiSamples", 1);
         data = capsule.readByteBufferArrayList("data", null);
         colorSpace = capsule.readEnum("colorSpace", ColorSpace.class, null);
-
         if (mipMapSizes != null) {
             needGeneratedMips = false;
             mipsWereGenerated = true;

+ 320 - 0
jme3-core/src/main/java/com/jme3/texture/TextureImage.java

@@ -0,0 +1,320 @@
+/*
+ * Copyright (c) 2024 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.texture;
+
+import com.jme3.renderer.opengl.GL2;
+import com.jme3.renderer.opengl.GL4;
+import com.jme3.renderer.opengl.TextureUtil;
+import java.util.Objects;
+
+/**
+ * Wraps a texture so that only a single level of the underlying image is bound
+ * instead of the entire image.
+ * 
+ * @author codex
+ */
+public class TextureImage {
+    
+    /**
+     * Enum specifying the shader access hint of the image.
+     * <p>
+     * Shader accesses that violate the hint may result in undefined behavior.
+     */
+    public enum Access {
+        
+        /**
+         * The image can only be read from in a shader.
+         */
+        ReadOnly(true, false, GL2.GL_READ_ONLY),
+        
+        /**
+         * The image can only be written to in a shader.
+         */
+        WriteOnly(false, true, GL2.GL_WRITE_ONLY),
+        
+        /**
+         * The image can both be written to and read from in a shader.
+         */
+        ReadWrite(true, true, GL2.GL_READ_WRITE);
+        
+        private final boolean read, write;
+        private final int glEnum;
+
+        private Access(boolean read, boolean write, int glEnum) {
+            this.read = read;
+            this.write = write;
+            this.glEnum = glEnum;
+        }
+        
+        /**
+         * If true, the image can be read from in a shader.
+         * 
+         * @return 
+         */
+        public boolean isRead() {
+            return read;
+        }
+
+        /**
+         * If true, the image can be written to in a shader.
+         * 
+         * @return 
+         */
+        public boolean isWrite() {
+            return write;
+        }
+        
+        /**
+         * Corresponding OpenGL enum.
+         * 
+         * @return 
+         */
+        public int getGlEnum() {
+            return glEnum;
+        }
+        
+    }
+    
+    private Texture texture;
+    private int level, layer;
+    private Access access;
+    private boolean updateFlag = true;
+    
+    public TextureImage(Texture texture) {
+        this(texture, 0, -1, Access.ReadWrite);
+    }
+    
+    public TextureImage(Texture texture, Access access) {
+        this(texture, 0, -1, access);
+    }
+    
+    public TextureImage(Texture texture, int level, int layer) {
+        this(texture, level, layer, Access.ReadWrite);
+    }
+    
+    public TextureImage(Texture texture, int level, int layer, Access access) {
+        this.texture = Objects.requireNonNull(texture, "Underlying texture cannot be null");
+        this.level = level;
+        this.layer = layer;
+        this.access = access;
+        if (this.level < 0) {
+            throw new IllegalArgumentException("Level cannot be less than zero.");
+        }
+    }
+    
+    /**
+     * Binds this texture image to the texture unit.
+     * <p>
+     * Calling this is not completely sufficient for totally binding an image
+     * to an image unit. Additionally, the image must be bound beforehand using
+     * {@link GL2#glBindTexture(int, int)}.
+     * 
+     * @param gl4 GL4 implementation (not null)
+     * @param texUtil utility used to convert JME's image format to the corresponding GL enum (not null)
+     * @param unit texture unit to bind to
+     */
+    public void bindImage(GL4 gl4, TextureUtil texUtil, int unit) {
+        Image img = texture.getImage();
+        gl4.glBindImageTexture(unit, img.getId(), level, isLayered(), Math.max(layer, 0),
+                access.getGlEnum(), texUtil.getImageFormat(img.getFormat(), false).internalFormat);
+    }
+    
+    /**
+     * Sets the update flag indicating this texture image needs rebinding.
+     */
+    public void setUpdateNeeded() {
+        updateFlag = true;
+    }
+    
+    /**
+     * Clears the update flag and returns the update flag's value before
+     * it was cleared.
+     * 
+     * @return 
+     */
+    public boolean clearUpdateNeeded() {
+        boolean f = updateFlag;
+        updateFlag = false;
+        return f;
+    }
+    
+    /**
+     * Sets the underlying texture wrapped by this TextureImage.
+     * 
+     * @param texture wrapped texture (not null)
+     */
+    public void setTexture(Texture texture) {
+        Objects.requireNonNull(texture, "Wrapped texture cannot be null.");
+        if (this.texture != texture) {
+            this.texture = texture;
+            updateFlag = true;
+        }
+    }
+    
+    /**
+     * Sets the image level to bind.
+     * <p>
+     * The level controls which mipmap level will be bound to the texture unit,
+     * where zero corresponds to the base level of the texture.
+     * <p>
+     * default=0
+     * 
+     * @param level level to bind (not negative)
+     */
+    public void setLevel(int level) {
+        if (level < 0) {
+            throw new IllegalArgumentException("Texture image level cannot be negative.");
+        }
+        if (this.level != level) {
+            this.level = level;
+            updateFlag = true;
+        }
+    }
+    
+    /**
+     * Sets the image layer to bind.
+     * <p>
+     * If the underlying texture is a one/two/three demensional array,
+     * cube map, cube map array, or two demensional multisample array,
+     * then this value specifies which layer of the array to bind.
+     * <p>
+     * default=-1
+     * 
+     * @param layer layer to bind, or negative to bind all layers
+     */
+    public void setLayer(int layer) {
+        if (this.layer != layer && (this.layer >= 0 || layer >= 0)) {
+            this.layer = layer;
+            updateFlag = true;
+        }
+    }
+    
+    /**
+     * Sets the shader access hint with which to bind the image.
+     * 
+     * @param access 
+     */
+    public void setAccess(Access access) {
+        if (this.access != access) {
+            this.access = access;
+            updateFlag = true;
+        }
+    }
+    
+    /**
+     * Gets the underlying texture wrapped by this TextureImage.
+     * 
+     * @return underlying texture
+     */
+    public Texture getTexture() {
+        return texture;
+    }
+    
+    /**
+     * Gets the image belonging to the underlying texture.
+     * 
+     * @return 
+     */
+    public Image getImage() {
+        return texture.getImage();
+    }
+    
+    /**
+     * Gets the format of the image belonging to the underlying texture.
+     * 
+     * @return 
+     */
+    public Image.Format getFormat() {
+        return texture.getImage().getFormat();
+    }
+    
+    /**
+     * Gets the native id of the image belonging to the underlying texture.
+     * 
+     * @return 
+     */
+    public int getImageId() {
+        return texture.getImage().getId();
+    }
+    
+    /**
+     * Gets the level.
+     * 
+     * @return 
+     * @see #setLevel(int)
+     */
+    public int getLevel() {
+        return level;
+    }
+    
+    /**
+     * Gets the layer.
+     * 
+     * @return 
+     * @see #setLayer(int)
+     */
+    public int getLayer() {
+        return layer;
+    }
+    
+    /**
+     * Gets the access hint.
+     * 
+     * @return 
+     * @see #setAccess(com.jme3.texture.TextureImage.Access)
+     */
+    public Access getAccess() {
+        return access;
+    }
+    
+    /**
+     * Returns true if all layers of an image will be bound, when
+     * {@code layer} is negative.
+     * 
+     * @return 
+     * @see #setLayer(int)
+     */
+    public boolean isLayered() {
+        return layer < 0;
+    }
+    
+    /**
+     * Returns true if the update flag has been set indicating rebinding
+     * is required.
+     * 
+     * @return 
+     */
+    public boolean isUpdateNeeded() {
+        return updateFlag;
+    }
+    
+}

+ 59 - 0
jme3-examples/src/main/java/jme3test/texture/TestShaderImage.java

@@ -0,0 +1,59 @@
+/*
+ * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
+ * Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template
+ */
+package jme3test.texture;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.material.Material;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.shape.Box;
+import com.jme3.scene.shape.Quad;
+import com.jme3.shader.VarType;
+import com.jme3.texture.Image;
+import com.jme3.texture.Texture2D;
+import com.jme3.texture.TextureImage;
+
+/**
+ *
+ * @author codex
+ */
+public class TestShaderImage extends SimpleApplication {
+    
+    private int frame = 0;
+    
+    public static void main(String[] args) {
+        new TestShaderImage().start();
+    }
+    
+    @Override
+    public void simpleInitApp() {
+        
+        Geometry box = new Geometry("Box", new Box(1, 1, 1));
+        Material mat = new Material(assetManager, "Materials/ImageTest.j3md");
+        box.setMaterial(mat);
+        rootNode.attachChild(box);
+        
+        int width = context.getFramebufferWidth();
+        int height = context.getFramebufferHeight();
+        Texture2D target = new Texture2D(width, height, Image.Format.RGBA8);
+        TextureImage targetImage = new TextureImage(target, TextureImage.Access.WriteOnly);
+        mat.setParam("TargetImage", VarType.Image2D, targetImage);
+        
+        Geometry pic = new Geometry("gui_pic", new Quad(200, 200));
+        pic.setLocalTranslation(0, height - 200, 0);
+        Material picMat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+        picMat.setTexture("ColorMap", target);
+        pic.setMaterial(mat);
+        guiNode.attachChild(pic);
+        
+    }
+    @Override
+    public void simpleUpdate(float tpf) {
+        if (frame++ < 5) {
+            cam.lookAt(Vector3f.ZERO, Vector3f.UNIT_Y);
+        }
+    }
+    
+}

+ 8 - 1
jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglGL.java

@@ -59,7 +59,14 @@ public final class LwjglGL implements GL, GL2, GL3, GL4 {
     public void glBindTexture(int param1, int param2) {
         GL11.glBindTexture(param1, param2);
     }
-
+    
+    @Override
+    public void glBindImageTexture(final int unit, final int texture, final int level,
+                                   final boolean layered, final int layer,
+                                   final int access, final int format) {
+        GL42.glBindImageTexture(unit, texture, level, layered, layer, access, format);
+    }
+    
     @Override
     public void glBlendEquationSeparate(int colorMode, int alphaMode){
         GL20.glBlendEquationSeparate(colorMode,alphaMode);

+ 9 - 1
jme3-lwjgl3/src/main/java/com/jme3/renderer/lwjgl/LwjglGL.java

@@ -80,7 +80,14 @@ public class LwjglGL extends LwjglRender implements GL, GL2, GL3, GL4 {
     public void glBindTexture(final int target, final int texture) {
         GL11.glBindTexture(target, texture);
     }
-
+    
+    @Override
+    public void glBindImageTexture(final int unit, final int texture, final int level,
+                                   final boolean layered, final int layer,
+                                   final int access, final int format) {
+        GL42.glBindImageTexture(unit, texture, level, layered, layer, access, format);
+    }
+    
     @Override
     public void glBlendEquationSeparate(final int colorMode, final int alphaMode) {
         GL20.glBlendEquationSeparate(colorMode, alphaMode);
@@ -653,4 +660,5 @@ public class LwjglGL extends LwjglRender implements GL, GL2, GL3, GL4 {
     public void glUniformBlockBinding(final int program, final int uniformBlockIndex, final int uniformBlockBinding) {
         GL31.glUniformBlockBinding(program, uniformBlockIndex, uniformBlockBinding);
     }
+    
 }

+ 11 - 0
jme3-testdata/src/main/resources/Materials/ImageTest.frag

@@ -0,0 +1,11 @@
+
+#import "Common/ShaderLib/GLSLCompat.glsllib"
+
+layout(RGBA8) uniform image2D m_TargetImage;
+
+void main() {
+    
+    gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
+    imageStore(m_TargetImage, ivec2(gl_FragCoord.xy), vec4(0.0, 1.0, 0.0, 1.0));
+    
+}

+ 20 - 0
jme3-testdata/src/main/resources/Materials/ImageTest.j3md

@@ -0,0 +1,20 @@
+MaterialDef ImageTest {
+
+    MaterialParameters {
+        
+        Image2D TargetImage
+        
+    }
+
+     Technique {
+        
+        VertexShader   GLSL400 GLSL320 GLSL150 : Common/MatDefs/Misc/Unshaded.vert
+        FragmentShader GLSL400 GLSL320 GLSL150 : Materials/ImageTest.frag
+
+        WorldParameters {
+            WorldViewProjectionMatrix
+        }
+        
+    }
+
+}