in my Libgdx Scene2D stage I am trying to have an actor flashing with white color.
With OpenGLES 2.0 I understood I needed to use a shader to achieve this and I have a problem implementing it.
My goal is to color an actor in solid white color given a speficic game event. The problem is given this specific event, everything in the stage becomes white (all actors and the background) instead of just the selected actors textures.
My World class is where the stage is created. It is rendered like this:
public void render() {
Gdx.gl.glClearColor(1, 1, 1, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
update();
stage.act(Gdx.graphics.getDeltaTime());
stage.draw();
}
In the stage I use actors that are defined in a MyActor class. This class has its own
draw method where I am trying to color the actor in solid white with the shader. Here is the draw method:
public void draw(Batch batch, float alpha) {
if (flashing) {
batch.setShader(shader);
}
batch.flush();
Color color = getColor();
batch.setColor(color.r, color.g, color.b, color.a * alpha);
batch.draw(tr, getX(), getY(), side/2, side/2, side, side, getScaleX(), getScaleY(), 0);
}
Obviously I do something wrong with the batch. Instead of coloring this one actor in white, it colors everything. Also it remains white even after the Boolean flashing = false;
Here is how I have set up my shader, in the actor class MyActor:
ShaderProgram shader;
String vertexShader =
"attribute vec4 a_position; \n" +
"attribute vec4 a_color;\n" +
"attribute vec2 a_texCoord0; \n" +
"uniform mat4 u_projTrans; \n" +
"varying vec4 v_color; \n" +
"varying vec2 v_texCoords; \n" +
"void main() { \n" +
"v_color = a_color; \n" +
"v_texCoords = a_texCoord0; \n" +
"gl_Position = u_projTrans * a_position; \n" +
"};";
String fragmentShader = "#ifdef GL_ES\n" +
"precision mediump float;\n" +
"#endif\n" + "varying vec4 v_color;\n" +
"varying vec2 v_texCoords;\n" +
"uniform sampler2D u_texture;\n" +
"uniform float grayscale;\n" +
"void main()\n" +
"{\n" +
"vec4 texColor = texture2D(u_texture, v_texCoords);\n" +
"float gray = dot(texColor.rgb, vec3(5, 5, 5));\n" +
"texColor.rgb = mix(vec3(gray), texColor.rgb, grayscale);\n" +
" gl_FragColor = v_color * texColor;\n" +
"}";
Then initiated with:
shader = new ShaderProgram(vertexShader, fragmentShader);
What is it that I am doing wrong?
I have to admit I probably don't understand correctly how the batch of MyActor, the drawing method and the rendering methods work together!
Thanks for your time
The problem here is, that you never set the Shader back to the default Shader, so your Spritebatch always uses the "solid white" Shader.
As the javadock of the setShader(ShaderProgram shader)sais you can reset the Shader by calling setShader(null).
Another possibility would be to use another Texture for the solid white and the simply say:
Texture t = flashing ? whiteTexture : defaultTexture;
and draw the Texture t.
Just a suggestion: Do not use batch.flush() if it is not absolutely neccessary, because flush() makes a draw call to the GPU and this costs some performance.
Related
I made simple viewer that allow you to import .obj file, what I want to achieve is, when user selecting the model, shaders will draw model outlines in different color.
What I use to represent the .obj file is a QEntity with Custom QMaterial, Custom QPickerObject and QMesh.
With my Custom QMaterial I made simple Flat Shading(coloring based on face normal)
// My Custom QMaterial :
explicit CustomizedMaterial(Qt3DCore::QNode *parent = nullptr) : QMaterial(parent)
{
// Create effect, technique, render pass and shader
Qt3DRender::QEffect *effect = new Qt3DRender::QEffect();
Qt3DRender::QTechnique *gl3Technique = new Qt3DRender::QTechnique();
Qt3DRender::QRenderPass *gl3Pass = new Qt3DRender::QRenderPass();
Qt3DRender::QShaderProgram *glShader = new Qt3DRender::QShaderProgram();
QByteArray ver(
"#version 330\n"
"out vec3 vViewPos;\n"
"in vec3 vertexPosition;\n"
"in vec3 vertexNormal;\n"
"uniform mat4 modelView;\n"
"uniform mat3 modelViewNormal;\n"
"uniform mat4 mvp;\n"
"void main()\n"
"{\n"
"vec4 pos = vec4(vertexPosition, 1.0);\n"
"vec4 mpos = modelView * pos;\n"
"gl_Position = mvp * vec4(vertexPosition, 1.0);\n"
"vViewPos = -mpos.xyz;\n"
"}\n");
QByteArray frag(
"#version 330\n"
"vec3 normals(vec3 pos)\n"
"{\n"
"vec3 fdx = dFdx(pos);\n"
"vec3 fdy = dFdy(pos);\n"
"return normalize(cross(fdx, fdy));\n"
"}\n"
"in vec3 vViewPos;\n"
"out vec4 fragColor;\n"
"void main()\n"
"{\n"
"vec3 normal = normals(vViewPos);\n"
"vec3 gray = vec3(0.9, 0.9, 0.9);\n"
"float theta = dot(normal, vec3(0, 0, 1)) / length(normal);\n"
"fragColor = vec4(gray * theta , 1.0);\n"
"}\n");
glShader->setVertexShaderCode(ver);
glShader->setFragmentShaderCode(frag);
// Set the shader on the render pass
gl3Pass->setShaderProgram(glShader);
// filter
Qt3DRender::QFilterKey *m_filterKey = new Qt3DRender::QFilterKey(this);
m_filterKey->setName(QStringLiteral("renderingStyle"));
m_filterKey->setValue(QStringLiteral("forward"));
// Add the pass to the technique
gl3Technique->addRenderPass(gl3Pass);
// Set the targeted GL version for the technique
gl3Technique->graphicsApiFilter()->setApi(Qt3DRender::QGraphicsApiFilter::OpenGL);
gl3Technique->graphicsApiFilter()->setMajorVersion(3);
gl3Technique->graphicsApiFilter()->setMinorVersion(2);
gl3Technique->graphicsApiFilter()->setProfile(Qt3DRender::QGraphicsApiFilter::CoreProfile);
// Add filter
gl3Technique->addFilterKey(m_filterKey);
// Add the technique to the effect
effect->addTechnique(gl3Technique);
// Set the effect on the materials
setEffect(effect);
}
From my searching I think the easiest way is by using two rendering pass technique, sadly there is no documentation or example in Qt3d/C++ show me how to do it, can some one help ?
Thanks in advance.
Yes there is not much information in this regard unfortunately. It is looks like that Qt put more effort and resource into QML and not C++ / all examples are 5 to 1 in favor of QML/.
Ok I manage to make custom shader work. I played with your code and changed just a few:
I moved the configuration of the QTechnique immediately after the creation plus I change the order of initialization of the technique:
gl3Technique->graphicsApiFilter()->setProfile(Qt3DRender::QGraphicsApiFilter::CoreProfile);
gl3Technique->graphicsApiFilter()->setApi(Qt3DRender::QGraphicsApiFilter::OpenGL);
gl3Technique->graphicsApiFilter()->setMajorVersion(3);
gl3Technique->graphicsApiFilter()->setMinorVersion(1);
I put QFilterKey for the technique
Qt3DRender::QFilterKey *filterkey = new Qt3DRender::QFilterKey(this);
filterkey->setName(QStringLiteral("renderingStyle"));
filterkey->setValue(QStringLiteral("forward"));
and I load shaders from resource the same way as is shown in example QML code :
glShader->setVertexShaderCode(Qt3DRender::QShaderProgram::loadSource(QUrl(QStringLiteral("qrc:/MyShader/simpleColor.vert"))));
glShader->setFragmentShaderCode(Qt3DRender::QShaderProgram::loadSource(QUrl(QStringLiteral("qrc:/MyShader/simpleColor.frag"))));
I did not investigate which one of those is the main reason but is working.
After that I found another confirmation - the same way of processing - in this post
How to make color of a section be different on a 3D object
from #AdaRaider and #user3405291
I am writing a simple video player using opengl. I used Qt and followed its basic texture examples.
The vertex and fragment shaders are here:
QOpenGLShader *vshader = new QOpenGLShader(QOpenGLShader::Vertex, this);
const char *vsrc =
"attribute highp vec4 vertex;\n"
"attribute mediump vec4 texCoord;\n"
"varying mediump vec4 texc;\n"
"uniform mediump mat4 matrix;\n"
"void main(void)\n"
"{\n"
" gl_Position = matrix * vertex;\n"
" texc = texCoord;\n"
"}\n";
vshader->compileSourceCode(vsrc);
QOpenGLShader *fshader = new QOpenGLShader(QOpenGLShader::Fragment, this);
const char *fsrc =
"uniform sampler2D texture;\n"
"varying mediump vec4 texc;\n"
"void main(void)\n"
"{\n"
" gl_FragColor = texture2D(texture, texc.st);\n"
"}\n";
fshader->compileSourceCode(fsrc);
And I did this to display a image:
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, texture_cv.cols, texture_cv.rows, GL_RGB, GL_UNSIGNED_BYTE, texture_cv.data);
//then draw a quad
...
Then after this how could I draw several red lines on the screen, since I am using the fragment shader (I am very new to shader), I cannot turn off the texture.
By far the easiest solution is to use a different shader program for drawing your red lines. Since it just draws a solid color, it will be very easy. The fragment shader could be something like:
void main()
{
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
The vertex shader will be very similar to what you have, except that it does not need to produce texture coordinates. You might even be able to use the existing vertex shader.
It is very common to use multiple shader programs for rendering. You have a shader program for each different type of rendering, and switch between them with glUseProgram().
I've been struggling with the shadow mapping for two days (with JOGL), yet still couldn't make it work. Now I just want to render a very simple shadow map (grass), where closer looks brighter and further looks darker, from the view of the light point.
Here is my code:
//setting up buffers
gl.glGenFramebuffers(1, framebuff);
gl.glBindFramebuffer(GL4.GL_FRAMEBUFFER, framebuff.get(0));
gl.glGenTextures(2, textureBuff);
gl.glBindTexture(GL4.GL_TEXTURE_2D, textureBuff.get(0));
gl.glTexStorage2D(GL4.GL_TEXTURE_2D, 1, GL4.GL_R32F, displayWidth, displayHeight);
gl.glTexParameteri(GL4.GL_TEXTURE_2D, GL4.GL_TEXTURE_MAG_FILTER, GL4.GL_LINEAR);
gl.glTexParameteri(GL4.GL_TEXTURE_2D, GL4.GL_TEXTURE_MIN_FILTER, GL4.GL_LINEAR_MIPMAP_LINEAR);
gl.glFramebufferTexture(GL4.GL_FRAMEBUFFER, GL4.GL_COLOR_ATTACHMENT0, textureBuff.get(0), 0);
gl.glBindTexture(GL4.GL_TEXTURE_2D, textureBuff.get(1));
gl.glTexStorage2D(GL4.GL_TEXTURE_2D, 1, GL4.GL_DEPTH_COMPONENT32F, displayWidth, displayHeight);
gl.glTexParameteri(GL4.GL_TEXTURE_2D, GL4.GL_TEXTURE_MAG_FILTER, GL4.GL_LINEAR);
gl.glTexParameteri(GL4.GL_TEXTURE_2D, GL4.GL_TEXTURE_MIN_FILTER, GL4.GL_LINEAR_MIPMAP_LINEAR);
gl.glTexParameteri(GL4.GL_TEXTURE_2D, GL4.GL_TEXTURE_COMPARE_MODE, GL4.GL_COMPARE_REF_TO_TEXTURE);
gl.glTexParameteri(GL4.GL_TEXTURE_2D, GL4.GL_TEXTURE_COMPARE_FUNC, GL4.GL_LEQUAL);
gl.glFramebufferTexture(GL4.GL_FRAMEBUFFER, GL4.GL_DEPTH_ATTACHMENT, textureBuff.get(1), 0);
gl.glDrawBuffer(GL4.GL_NONE);
if(gl.glCheckFramebufferStatus(GL4.GL_FRAMEBUFFER) != GL4.GL_FRAMEBUFFER_COMPLETE)
System.out.println(gl.glCheckFramebufferStatus(GL4.GL_FRAMEBUFFER));
Vertex shader:
"#version 430 \n" +
"layout (location = 3) uniform mat4 mvMatrix; \n" +
"layout (location = 4) uniform mat4 proMatrix; \n" +
"layout (location = 0) in vec4 position; \n" +
" \n" +
"void main(void) \n" +
"{ \n" +
" randomize position... \n" +
" \n" +
" gl_Position = proMatrix * mvMatrix * position; \n" +
"}"
Fragment shader code:
"#version 430 \n" +
"out vec4 output_color; \n" +
"void main(void) \n" +
"{ \n" +
" output_color = vec4(gl_FragCoord.z); \n" +
"}"
drawing command (not sure if it's correct):
//gl.glBindFramebuffer(GL4.GL_FRAMEBUFFER, framebuff.get(0));
gl.glViewport(0, 0, displayWidth, displayWidth);
gl.glEnable(GL4.GL_POLYGON_OFFSET_FILL);
gl.glPolygonOffset(2.0f, 4.0f);
/*IntBuffer frameType = GLBuffers.newDirectIntBuffer(1);
frameType.put(GL4.GL_COLOR_ATTACHMENT0);
gl.glDrawBuffers(1, frameType);
gl.glClearBufferfv(GL4.GL_COLOR, 0, new float[] {0, 0, 0}, 0);
gl.glClearDepth(1.0f);*/
setupMVPMatrix();
gl.glBindVertexArray(vaoBuff.get(0));
gl.glUseProgram(shaderProgram);
gl.glDrawArraysInstanced(GL4.GL_TRIANGLE_STRIP, 0, 5, 512 * 512);
gl.glDisable(GL4.GL_POLYGON_OFFSET_FILL);
//gl.glBindFramebuffer(GL4.GL_FRAMEBUFFER, 0);
added:
when I comment the glBindFramebuffer(), the grass appears correctly with the white color (from the light point of view, which shows the matrix should be correct).
But if I call glBindFramebuffer() with depth test enabled, everything just disappeared while I expect the closer grass to be brighter and further grass to be darker.(also checked the framebuffer status, nothing wrong)
Any help?
Fixed, I didn't understand the meaning of off-screen rendering at the beginning. Just render the depth value stored in the texture into the default framebuffer for display, and everything work just fine. I can't believe I spent 5 days on this.
gl.glBindFramebuffer(GL4.GL_FRAMEBUFFER, framebuff.get(0));
draw scene...(storing the depth value into the depth texture)
gl.glBindFramebuffer(GL4.GL_FRAMEBUFFER, 0);
draw scene...(comparing with the depth texture stored)
I'm trying to create a "transition" effect between two 2D scenes. I have 3 textures: before, after, and mask. before and after are self-explanatory. mask is a simple monochrome texture that defines how the first two get composited. It changes over time, to perform the transition. All 3 textures are the same size.
I've verified that all 3 textures contain the correct data, but when I try to perform the compositing, I end up with either before in its entirety, or after in its entirety, seemingly at random.
Here's what I'm doing:
Application code:
glEnable(GL_MULTISAMPLE);
glActiveTextureARB(GL_TEXTURE1_ARB);
glEnable(GL_TEXTURE_RECTANGLE_ARB);
after.handle.bind;
glActiveTextureARB(GL_TEXTURE2_ARB);
glEnable(GL_TEXTURE_RECTANGLE_ARB);
mask.handle.bind;
glActiveTextureARB(GL_TEXTURE0_ARB);
before.handle.bind;
GShaders.UseShaderProgram(maskProgramHandle); //GShaders: Global shader engine
GShaders.SetUniformValue(maskProgramHandle, 'before', 0);
GShaders.SetUniformValue(maskProgramHandle, 'after', 1);
GShaders.SetUniformValue(maskProgramHandle, 'mask', 2);
before.DrawFull; //draws the texture to the screen as a quad.
glDisable(GL_MULTISAMPLE);
Vertex shader:
varying vec4 v_color;
varying vec2 texture_coordinate;
void main()
{
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
texture_coordinate = vec2(gl_MultiTexCoord0);
v_color = gl_Color;
gl_FrontColor = gl_Color;
}
Fragment shader:
uniform sampler2DRect before;
uniform sampler2DRect after;
uniform sampler2DRect mask;
varying vec2 texture_coordinate;
void main()
{
vec3 maskValue = texture2DRect(mask, texture_coordinate).rgb;
float alpha = (maskValue.r + maskValue.g + maskValue.b) / 3.0;
vec4 beforeValue = texture2DRect(before, texture_coordinate);
vec4 afterValue = texture2DRect(after, texture_coordinate);
gl_FragColor = mix(beforeValue, afterValue, alpha);
}
Any idea what's going wrong?
This is only guessing, have you tried this
gl_FragColor = mix(beforeValue, afterValue, alpha / 255.0f);
I'm trying to add an uniform var to my shader , this is my shader:
String vertexShader = "attribute vec4 " + ShaderProgram.POSITION_ATTRIBUTE + ";\n" //
+ "attribute vec4 " + ShaderProgram.COLOR_ATTRIBUTE + ";\n" //
+ "attribute vec2 " + ShaderProgram.TEXCOORD_ATTRIBUTE + "0;\n" //
+ "uniform mat4 u_projTrans;\n" //
+ "varying vec4 v_color;\n" //
+ "varying vec2 v_texCoords;\n" //
+ "\n" //
+ "void main()\n" //
+ "{\n" //
+ " v_texCoords = " + ShaderProgram.TEXCOORD_ATTRIBUTE + "0;\n" //
+ " gl_Position = u_projTrans * " + ShaderProgram.POSITION_ATTRIBUTE + ";\n" //
+ "}\n";
String fragmentShader = "#ifdef GL_ES\n" //
+ "#define LOWP lowp\n" //
+ "precision mediump float;\n" //
+ "#else\n" //
+ "#define LOWP \n" //
+ "#endif\n" //
+ "varying vec2 v_texCoords;\n" //
+ "uniform sampler2D u_texture;\n" //
+ "uniform sampler2D u_texturePalette;\n" //
+ "void main()\n"//
+ "{\n" //
+ " vec4 textureColor = texture2D(u_texture, v_texCoords).rgba;\n"
+ " vec4 paletteColor = texture2D(u_texturePalette, vec2(textureColor.b,0)).rgba;\n"
+ " gl_FragColor = paletteColor;\n" //
+ "}";
The problem comes with
uniform sampler2D u_texturePalette;
ShaderProgram doesn't recognize the new uniform in the shader. Also i have done this :
shader = new ShaderProgram(vertexShader, fragmentShader);
shader.setUniformi("u_texturePalette", 0);
Texture text = new Texture(Gdx.files.internal("./data/palette.png"));
text.bind(shader.getUniformLocation("u_texturePalette"));
batch = new SpriteBatch();
batch.setShader(shader);
When i call function shader.hasUniform("u_texturePalette") , it resolves to true , but when i go to see shader.getUniformLocation("u_texturePalette") it resolves to -1
Is this a bug? . Any idea of what i'm doing bad?
Shader code looks fine.
Generally you do it like this:
shader = new ShaderProgram(vertexShader, fragmentShader);
Texture text = new Texture(Gdx.files.internal("./data/palette.png"));
text.bind(0);
shader.setUniformi("u_texturePalette", 0);
So you bind the texture to an active texture unit and then pass that unit via setUniformi().
getUniformLocation() does actually something different: https://www.khronos.org/opengles/sdk/docs/man/xhtml/glGetUniformLocation.xml
However you will run into some more issues here, since SpriteBatch internally does some very specific stuff with the custom shader. I'd recommend you take a look at the code:
https://github.com/libgdx/libgdx/blob/master/gdx/src/com/badlogic/gdx/graphics/g2d/SpriteBatch.java
Specifically it will expect the default texture to always be at unit 0.