I have got shadow mapping working nice on android but there is a problem - shadows 'leak'. So imagine a cylinder imbedded in the floor of a terrain with some portion under that terrain. The portion above creates a nice shadow opposite to the light source but the potion under the terrain casts a shadow forwards - towards the light. Its as if there is no depth buffer.
Here is my frambuffer code (where i think the problem is).
MyGLRenderer.shadowsize=4096;
GLES20.glGenTextures(1, temp2qshadow, 0);
LoadIntroPics.fboTexqshadow = temp2qshadow[0];
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, fboTexqshadow);
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, MyGLRenderer.shadowsize, MyGLRenderer.shadowsize/2, 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
GLES20.glGenFramebuffers(1, tempqshadow, 0);
MyGLRenderer.fboIdshadow = tempqshadow[0];
GLES20.glGenRenderbuffers(1, temp3qshadow, 0);
LoadIntroPics.renderBufferIdqshadow = temp3qshadow[0];
GLES20.glBindRenderbuffer(GLES20.GL_RENDERBUFFER, renderBufferIdqshadow);
GLES20.glRenderbufferStorage(GLES20.GL_RENDERBUFFER, GLES20.GL_DEPTH_COMPONENT16, MyGLRenderer.shadowsize, MyGLRenderer.shadowsize/2);
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, MyGLRenderer.fboIdshadow);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, LoadIntroPics.fboTexqshadow);
GLES20.glFramebufferRenderbuffer(GLES20.GL_FRAMEBUFFER, GLES20.GL_DEPTH_ATTACHMENT, GLES20.GL_RENDERBUFFER,LoadIntroPics.renderBufferIdqshadow);
GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D, LoadIntroPics.fboTexqshadow, 0);
And this is the shadow map code....
protected String getVertexShaderShadow()//this needs a model view projection matrix
{
final String vertexShader =
"precision highp float; \n"
+"attribute vec4 a_Position; \n"
+"uniform mat4 u_MVPMatrix; \n"
//+"uniform mat4 u_ShadowMatrix; \n"
+"attribute vec2 a_TexCoordinate; \n"
+"varying vec2 v_TexCoordinate; \n"
+"varying vec4 v_v4TexCoord; \n"
+"void main() \n"
+"{ \n"
+"v_TexCoordinate = a_TexCoordinate; \n"
+"gl_Position =u_MVPMatrix * a_Position; \n"
+"v_v4TexCoord = u_MVPMatrix * a_Position; \n"
+"} \n";
return vertexShader;
}
protected void getFragmentShaderShadow()
{
final String fragmentShader =
"precision highp float; \n"
+"uniform sampler2D u_Texture; \n"
+"uniform int decal; \n"
+"varying vec2 v_TexCoordinate; \n"
+"varying vec4 v_v4TexCoord; \n"
+"void main() \n"
+"{ \n"
+"vec4 col=texture2D(u_Texture, v_TexCoordinate); \n"
+"if(decal==1){if(col.r<=0.25 && col.g<=0.25 && col.b<=0.25)discard;} \n"
+"if(decal==2){if(col.r<=0.05 && col.g<=0.05 && col.b<=0.05)discard;} \n"
/* Generate shadow map - write fragment depth. */
+"float value = 10.0 - v_v4TexCoord.z; \n"
+"float v = floor(value); \n"
+"float f = value - v; \n"
+"float vn = v * 0.1; \n"
+"gl_FragColor = vec4(vn, f, 0.0, 1.0); \n"
//+"gl_FragColor=vec4(1.0,1.0,1.0,1.0); \n"
+"} \n";
fragmentShaderHandle =compileShader(GLES20.GL_FRAGMENT_SHADER, fragmentShader);
MyGLRenderer.mPerVertexProgramHandleShadow = createAndLinkProgram(MyGLRenderer.vertexShaderHandleShadow, fragmentShaderHandle,
new String[] {"a_Position", "a_Color", "a_Normal", "a_TexCoordinate"});
}
And ideas? Thanks
Here is a snapshot.
Related
I want to add a very simply lighting effect in my models via shaders. I found out there a vertex and a fragment shaders that make the work on OpenGL:
static const char* vertSource = {
"uniform vec3 lightPosition;\n"
"varying vec3 normal, eyeVec, lightDir;\n"
"void main()\n"
"{\n"
" vec4 vertexInEye = gl_ModelViewMatrix * gl_Vertex;\n"
" eyeVec = -vertexInEye.xyz;\n"
" lightDir = vec3(lightPosition - vertexInEye.xyz);\n"
" normal = gl_NormalMatrix * gl_Normal;\n"
" gl_Position = ftransform();\n"
"}\n"
};
static const char* fragSource = {
"uniform vec4 lightDiffuse;\n"
"uniform vec4 lightSpecular;\n"
"uniform float shininess;\n"
"varying vec3 normal, eyeVec, lightDir;\n"
"void main (void)\n"
"{\n"
" vec4 finalColor = gl_FrontLightModelProduct.sceneColor;\n"
" vec3 N = normalize(normal);\n"
" vec3 L = normalize(lightDir);\n"
" float lambert = dot(N,L);\n"
" if (lambert > 0.0)\n"
" {\n"
" finalColor += lightDiffuse * lambert;\n"
" vec3 E = normalize(eyeVec);\n"
" vec3 R = reflect(-L, N);\n"
" float specular = pow(max(dot(R, E), 0.0), shininess);\n"
" finalColor += lightSpecular * specular;\n"
" }\n"
" gl_FragColor = finalColor;\n"
"}\n"
};
The problem is that I am working in OpenGL ES2, because I am developing an Android app. And it seems that the in-built variable gl_FrontLightModelProduct is not available for GLES20, because I am having compilation fails in this line.
My question therefore is: How I can modify the above shaders to make them work in a OpenGL ES20 context?
gl_FrontLightModelProduct.sceneColor gives the Ambient colour of the scene which can be 0 if you want the area which is not affected by light to be fully black. You can replace that with a vec4(0.0, 0.0, 0.0, 1.0);
You should also remove these variables and send them as uniforms.
gl_ModelViewMatrix (send as uniform)
gl_Vertex (read this from attributes)
gl_NormalMatrix (send as uniform)
gl_Normal (read this from attributes)
Or instead of fighting in converting OpenGL shader, you can search for Simple OpenGL ES 2.0 shaders
I am learning opengl es 2, and I have a sphere with a texture. I have no problems to see it from outside, but if i put the camera inside, all I see is dark. I tried to solve this with:
GLES20.glCullFace(GLES20.GL_FRONT_AND_BACK);
But I still see a black screen. It is due to light? what can I do to see the texture from inside the sphere?
These are the shaders:
final String vertexShader =
"uniform mat4 u_MVPMatrix; \n" // A constant representing the combined model/view/projection matrix.
+ "attribute vec4 a_Position; \n" // Per-vertex position information we will pass in.
+ "varying vec2 vTexCoord; \n"
+ "attribute vec2 vTexCoord0; \n"
+ "void main() \n" // The entry point for our vertex shader.
+ "{ \n"
+ " gl_Position = u_MVPMatrix \n" // gl_Position is a special variable used to store the final position.
+ " * a_Position; \n" // Multiply the vertex by the matrix to get the final point in
+ " vTexCoord = vTexCoord0; \n"
+ "} \n"; // normalized screen coordinates.
final String fragmentShader =
"precision mediump float; \n"
+ "varying vec2 vTexCoord; \n"
+ "uniform sampler2D sTexture; \n"
+ "void main() \n"
+ "{ \n"
+ " gl_FragColor = texture2D( sTexture, vTexCoord );\n"
+ "}
GLES20.glCullFace(GLES20.GL_FRONT_AND_BACK);
tells it to cull (not show) both front and back-facing tris. You shouldn't see anything from either inside or outside with that setting. Try "NONE" instead.
GLES20.glCullFace(GLES20.GL_FRONT_AND_BACK); means you want it to cull (i.e. not render) both the front AND back. It's hard to imagine a situation where anyone would ever want to do that.
To turn off culling in C, you'd need to do
glDisable(GL_CULL_FACE);
I presume for your Java binding the command looks like
GLES20.glDisable(GLES20.GL_CULL_FACE);
Can someone please advise what's wrong with the following OpenGL ES 2.0 code? I'm using the Vuforia Augmented Reality SDK and I want to draw line with one end one colour, then fading into the colour of the other end.
GLES20.glUseProgram(shaderProgramID);
GLES20.glVertexAttribPointer(vertexHandle, 3, GLES20.GL_FLOAT, false, 0, line_vertices_buffer);
GLES20.glEnableVertexAttribArray(vertexHandle);
GLES20.glVertexAttribPointer(colorHandle, 3, GLES20.GL_FLOAT, false, 0, line_colours_buffer);
GLES20.glEnableVertexAttribArray(colorHandle);
GLES20.glBindAttribLocation(shaderProgramID, colorHandle, "color");
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextures.get(2).mTextureID[0]);
Matrix.multiplyMM(modelViewProjection, 0, vuforiaAppSession.getProjectionMatrix().getData(), 0, modelViewMatrix, 0);
GLES20.glEnable(GLES20.GL_BLEND);
GLES20.glUniformMatrix4fv(mvpMatrixHandle, 1, false, modelViewProjection, 0);
GLES20.glUniform1i(colorHandle, 0);
GLES20.glLineWidth(30);
GLES20.glDrawArrays(GLES20.GL_LINES, 0, 2 + line_vertices_array.size());
GLES20.glDisable(GLES20.GL_BLEND);
GLES20.glDisableVertexAttribArray(vertexHandle);
GLES20.glDisableVertexAttribArray(colorHandle);
The gradient doesn't appear. Ubuntu Eclipse LogCat states the following error which is related to the glUniform1i(colorHandle, 0) command near the end of the code.
06-06 09:13:57.340: E/Vuforia_Sample_Applications(14175): After operation UserDefinedTargets renderFrame got glError 0x502
06-06 09:13:57.390: W/Adreno-ES20(14175): <__load_uniform_int:351>: GL_INVALID_OPERATION
Does anyone know how to fix this error?
The shaders are given by:
String frameVertexShader = " \n" + "attribute vec4 vertexPosition; \n"
+ "attribute vec2 vertexTexCoord; \n" + "\n"
+ "varying vec2 texCoord; \n" + "\n"
+ "uniform mat4 modelViewProjectionMatrix; \n" + "\n"
+ "void main() \n" + "{ \n"
+ "gl_Position = modelViewProjectionMatrix * vertexPosition; \n"
+ "texCoord = vertexTexCoord; \n" + "} \n";
String frameFragmentShader = " \n" + "precision mediump float; \n" + "\n"
+ "varying vec2 texCoord; \n" + "\n"
+ "uniform sampler2D texSampler2D; \n" + "uniform vec4 keyColor; \n"
+ "\n" + "void main() \n" + "{ \n"
+ "vec4 texColor = texture2D(texSampler2D, texCoord); \n"
+ "gl_FragColor = keyColor * texColor; \n" + "} \n" + "";
I tried changing "color" to "keyColor" in my main code, but still get the error, i.e.
GLES20.glBindAttribLocation(shaderProgramID, colorHandle, "color");
to
GLES20.glBindAttribLocation(shaderProgramID, colorHandle, "keyColor");
I also made sure my buffer has 4 floats for RGBA since the fragment shader seems to expect this in keyColor.
NB Vuforia does seem to use ByteBuffer even for the float array, and then we use putFloat into the ByteBuffer, e.g.
line_colours_buffer = ByteBuffer.allocateDirect(4 * 4 * (2 + line_vertices_array.size()));
line_colours_buffer.order(ByteOrder.LITTLE_ENDIAN);
line_colours_buffer.putFloat(1f); // colour for line start
line_colours_buffer.putFloat(0f);
line_colours_buffer.putFloat(0f);
line_colours_buffer.putFloat(0.5f);
line_colours_buffer.putFloat(0f); // colour for line start
line_colours_buffer.putFloat(1f);
line_colours_buffer.putFloat(0f);
line_colours_buffer.putFloat(0.5f);
... + other lines as required.
Here is the fragment shader code I am trying to use (in OpenGL ES 2.0, OpenGLES GLSL ES 1.00):
GLchar strFragmentShader[] =
"precision mediump float; \n"
"varying vec2 vTexCoord; \n"
"uniform sampler2D sTexture; \n"
"uniform float offset[] = float[]( 0.0000, 1.3846, 3.2307); \n"
"uniform float weight[] = float[]( 0.2270, 0.3162, 0.0702); \n"
"void main() \n"
"{ \n"
" vec4 sum = texture2D( sTexture, vec2(vTexCoord)/1024.0)*weight[0]; \n"
" for (int i=0;i<3;i++) { \n"
" sum += texture2D( sTexture, ( vec2(vTexCoord+vec2(0.0,offset[i])/1024.0 )*weight[i]; \n"
" sum += texture2D( sTexture, ( vec2(vTexCoord-vec2(0.0,offset[i])/1024.0 )*weight[i]; \n"
" } \n"
" gl_FragColor = sum; \n"
"} \n";
I am basically following the examples on this page, but I get ERROR:LEX/PARSE-1 (fragment shader, line 4) Syntax error.
If I try to declare them as const rather than uniform as suggested here (under Array Constructors), I get ERROR:CUSTOM-5 (fragment shader, line 4) Array cannot be const.
Obviously lines 4 and 5 will have the same problem. How do I get this to compile? What is the correct syntax?
I'd do it like this:
GLchar strFragmentShader[] =
"precision mediump float; \n"
"varying vec2 vTexCoord; \n"
"uniform sampler2D sTexture; \n"
"void main() \n"
"{
" vec3 offset = vec3(0.0000, 1.3846, 3.2307); \n"
" vec3 weight = vec3( 0.2270, 0.3162, 0.0702); \n" \n"
" vec4 sum = texture2D( sTexture, vec2(vTexCoord)/1024.0)*weight[0]; \n"
" for (int i=0;i<3;i++) { \n"
" sum += texture2D( sTexture, ( vec2(vTexCoord+vec2(0.0,offset[i])/1024.0 )*weight[i]; \n"
" sum += texture2D( sTexture, ( vec2(vTexCoord-vec2(0.0,offset[i])/1024.0 )*weight[i]; \n"
" } \n"
" gl_FragColor = sum; \n"
"}"
or this if you need it passed to the shader as a uniform:
GLchar strFragmentShader[] =
"precision mediump float; \n"
"varying vec2 vTexCoord; \n"
"uniform sampler2D sTexture; \n"
"uniform vec3 offset; \n"
"uniform vec3 weight; \n"
"void main() \n"
"{ \n"
" vec4 sum = texture2D( sTexture, vec2(vTexCoord)/1024.0)*weight[0]; \n"
" for (int i=0;i<3;i++) { \n"
" sum += texture2D( sTexture, ( vec2(vTexCoord+vec2(0.0,offset[i])/1024.0 )*weight[i]; \n"
" sum += texture2D( sTexture, ( vec2(vTexCoord-vec2(0.0,offset[i])/1024.0 )*weight[i]; \n"
" } \n"
" gl_FragColor = sum; \n"
"}"
or use uniform float[n] where n is the desired (constant) size in the second example if you need an array, or const float[n] in the first example if you need an array and you don't want to pass the value from your C++ code.
Turns out that this is just a limitation of GLES. It can't be done. An alternative method needs to be found.
setShaders
muOffsetHandle = getUniformLocation("offset");
muWeightHandle = getUniformLocation("weight");
useProgram
GLES20.glUniform1fv(muOffsetHandle, 1, mOffset, 0);
GLES20.glUniform1fv(muWeightHandle, 1, mWeight, 0);
vars
private int muOffsetHandle;
private int muWeightHandle;
protected float mOffset[] =new float[] {0.0f, 1.3f, 3.3f};
protected float mWeight[] =new float[] {0.2f, 0.3f, 0.3f};
FragmentShader
"uniform float offset[3];\n" +
"uniform float weight[3];\n" +
then trying to reach: weight[i]
I get this:
Shader log: 0:13: S0004: Member reference or swizzle attempted on non-structure and non-vector
line 13: tc += texture2D(uFrameBufferTexture, vTextureCoord[0].xy + vec2(0.0, offset[i])/uScreenHeight).rgb * weight[i];
(i = 0 to 2)
so my question: How to bypassing a float array to a uniform? float[3]
the code
protected static final String mFShader =
"precision mediump float;\n" +
"varying vec2 vTextureCoord;\n" +
"uniform float uTime;\n" +
"uniform float uScreenWidth;\n" +
"uniform float uScreenHeight;\n" +
"uniform sampler2D uFrameBufferTexture;\n"+
"uniform float offset[3];\n" +
"uniform float weight[3];\n" +
"void main() {\n"+
" vec3 tc = vec3(1.0, 0.0, 0.0);\n"+
" if (vTextureCoord[0].x<(uTime-0.01)) {\n"+
" tc = texture2D(uFrameBufferTexture, vTextureCoord[0].xy).rgb * weight[0];\n"+
" for (int i=1; i<3; i++) {\n"+
" tc += texture2D(uFrameBufferTexture, vTextureCoord[0].xy + vec2(0.0, offset[i])/uScreenHeight).rgb * weight[i];\n"+
" tc += texture2D(uFrameBufferTexture, vTextureCoord[0].xy - vec2(0.0, offset[i])/uScreenHeight).rgb * weight[i];\n"+
" }\n"+
" }\n"+
" else if (vTextureCoord[0].x>=(uTime+0.01)) {\n"+
" tc = texture2D(uFrameBufferTexture, vTextureCoord[0].xy).rgb;\n"+
" }\n"+
" gl_FragColor = vec4(tc, 1.0);\n"+
"}\n";
The problem isn't actually with weight or offset. The compiler is complaining that you say vTextureCoord[0].xy, but you declared vTextureCoord as varying vec2 vTextureCoord;.
Either declare vTextureCoord as an array, or don't say [0].
You don't need to declare it as an array, but if you want you can simply use:
varying vec4 vTextureCoord[3]; // you can use anything you want between the brackets
Also, don't forget to modify the varying the the vertex shader too so that they will be identical.