diff --git a/jme3-core/src/main/java/com/jme3/material/Material.java b/jme3-core/src/main/java/com/jme3/material/Material.java index 86e8ecca5e..50807db935 100644 --- a/jme3-core/src/main/java/com/jme3/material/Material.java +++ b/jme3-core/src/main/java/com/jme3/material/Material.java @@ -50,6 +50,7 @@ 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 MatParamTexture getTextureParam(String name) { } return null; } - + /** * Returns a collection of all parameters set on this material. * @@ -514,6 +515,10 @@ public void setParam(String name, VarType type, Object value) { if (technique != null) { technique.notifyParamChanged(name, type, value); } + if (type.isImageType()) { + // recompute sort id + sortingId = -1; + } } } @@ -859,9 +864,13 @@ private void updateShaderMaterialParameter(Renderer renderer, VarType type, Shad 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(); diff --git a/jme3-core/src/main/java/com/jme3/renderer/Renderer.java b/jme3-core/src/main/java/com/jme3/renderer/Renderer.java index c92de923ae..acd0d599e1 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/Renderer.java +++ b/jme3-core/src/main/java/com/jme3/renderer/Renderer.java @@ -42,6 +42,7 @@ 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. diff --git a/jme3-core/src/main/java/com/jme3/renderer/opengl/GL2.java b/jme3-core/src/main/java/com/jme3/renderer/opengl/GL2.java index 89fea8c88c..21be3e5c39 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/opengl/GL2.java +++ b/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; /** *

Reference Page - This function is deprecated and unavailable in the Core profile

diff --git a/jme3-core/src/main/java/com/jme3/renderer/opengl/GL4.java b/jme3-core/src/main/java/com/jme3/renderer/opengl/GL4.java index 04ad52b2dd..17c0bc4f1c 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/opengl/GL4.java +++ b/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); + } diff --git a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java index 60e6b30265..811bab583c 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java +++ b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java @@ -61,6 +61,7 @@ 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 void setTexture(int unit, Texture tex) throws TextureUnitException { 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 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()) { diff --git a/jme3-core/src/main/java/com/jme3/renderer/opengl/TextureUtil.java b/jme3-core/src/main/java/com/jme3/renderer/opengl/TextureUtil.java index 11ce437e27..7064f7ce2e 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/opengl/TextureUtil.java +++ b/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 @@ * * @author Kirill Vainer */ -final class TextureUtil { +public final class TextureUtil { private static final Logger logger = Logger.getLogger(TextureUtil.class.getName()); diff --git a/jme3-core/src/main/java/com/jme3/shader/VarType.java b/jme3-core/src/main/java/com/jme3/shader/VarType.java index 1c22d45525..ebf7edc64c 100644 --- a/jme3-core/src/main/java/com/jme3/shader/VarType.java +++ b/jme3-core/src/main/java/com/jme3/shader/VarType.java @@ -43,9 +43,10 @@ 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 Class[] getJavaType() { public boolean isTextureType() { return textureType; } + + public boolean isImageType() { + return imageType; + } public boolean usesMultiData() { return usesMultiData; diff --git a/jme3-core/src/main/java/com/jme3/system/NullRenderer.java b/jme3-core/src/main/java/com/jme3/system/NullRenderer.java index 978a24211e..28eb6b5231 100644 --- a/jme3-core/src/main/java/com/jme3/system/NullRenderer.java +++ b/jme3-core/src/main/java/com/jme3/system/NullRenderer.java @@ -48,6 +48,7 @@ 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 void deleteFrameBuffer(FrameBuffer fb) { 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 void setShaderStorageBufferObject(int bindingPoint, BufferObject bufferOb public void setUniformBufferObject(int bindingPoint, BufferObject bufferObject) { } + } diff --git a/jme3-core/src/main/java/com/jme3/texture/Image.java b/jme3-core/src/main/java/com/jme3/texture/Image.java index 25518966a2..3159f0856a 100644 --- a/jme3-core/src/main/java/com/jme3/texture/Image.java +++ b/jme3-core/src/main/java/com/jme3/texture/Image.java @@ -1299,7 +1299,6 @@ public void read(JmeImporter importer) throws IOException { multiSamples = capsule.readInt("multiSamples", 1); data = capsule.readByteBufferArrayList("data", null); colorSpace = capsule.readEnum("colorSpace", ColorSpace.class, null); - if (mipMapSizes != null) { needGeneratedMips = false; mipsWereGenerated = true; diff --git a/jme3-core/src/main/java/com/jme3/texture/TextureImage.java b/jme3-core/src/main/java/com/jme3/texture/TextureImage.java new file mode 100644 index 0000000000..31b705fa5d --- /dev/null +++ b/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. + *

+ * 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. + *

+ * 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. + *

+ * The level controls which mipmap level will be bound to the texture unit, + * where zero corresponds to the base level of the texture. + *

+ * 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. + *

+ * 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. + *

+ * 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; + } + +} diff --git a/jme3-examples/src/main/java/jme3test/texture/TestShaderImage.java b/jme3-examples/src/main/java/jme3test/texture/TestShaderImage.java new file mode 100644 index 0000000000..52f698c99d --- /dev/null +++ b/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); + } + } + +} diff --git a/jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglGL.java b/jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglGL.java index 2dcf289584..97a806c3e3 100644 --- a/jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglGL.java +++ b/jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglGL.java @@ -59,7 +59,14 @@ public void glBindBuffer(int param1, int param2) { 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); diff --git a/jme3-lwjgl3/src/main/java/com/jme3/renderer/lwjgl/LwjglGL.java b/jme3-lwjgl3/src/main/java/com/jme3/renderer/lwjgl/LwjglGL.java index 6c070c83da..d2c607d747 100644 --- a/jme3-lwjgl3/src/main/java/com/jme3/renderer/lwjgl/LwjglGL.java +++ b/jme3-lwjgl3/src/main/java/com/jme3/renderer/lwjgl/LwjglGL.java @@ -80,7 +80,14 @@ public void glBindBuffer(final int target, final int buffer) { 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 void glBindBufferBase(final int target, final int index, final int buffer public void glUniformBlockBinding(final int program, final int uniformBlockIndex, final int uniformBlockBinding) { GL31.glUniformBlockBinding(program, uniformBlockIndex, uniformBlockBinding); } + } diff --git a/jme3-testdata/src/main/resources/Materials/ImageTest.frag b/jme3-testdata/src/main/resources/Materials/ImageTest.frag new file mode 100644 index 0000000000..5e2339f06a --- /dev/null +++ b/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)); + +} diff --git a/jme3-testdata/src/main/resources/Materials/ImageTest.j3md b/jme3-testdata/src/main/resources/Materials/ImageTest.j3md new file mode 100644 index 0000000000..0e11e66319 --- /dev/null +++ b/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 + } + + } + +}