I try to write a small program in OpenGL ES 2.0. But i found i quit hard to inspect the variable in shader language.
For example i want to know the value in ver vertex shader. I will pass the value to fragment shader and put the value as the Red in glFragColor. But i found it quit hard to pass the value. If i declare the value using varying, then the value will change.
Here is the code, the log is the value i want to print.
public static final String VERTEX_SHADER =
"attribute vec4 position;\n" +
"attribute vec2 inputTextureCoordinate;\n" +
"\n" +
"uniform float texelWidthOffset; \n" +
"uniform float texelHeightOffset; \n" +
"\n" +
"varying vec2 centerTextureCoordinate;\n" +
"varying vec2 oneStepLeftTextureCoordinate;\n" +
"varying vec2 twoStepsLeftTextureCoordinate;\n" +
"varying vec2 oneStepRightTextureCoordinate;\n" +
"varying vec2 twoStepsRightTextureCoordinate;\n" +
"varying float log;\n" +
"\n" +
"void main()\n" +
"{\n" +
"log = -0.1;\n" +
"gl_Position = position;\n" +
"vec2 firstOffset;\n" +
"vec2 secondOffset;\n" +
// "if (sqrt(pow(position.x, 2) + pow(position.y, 2)) < 0.2) {\n" +
// "log = -position.x;\n" +
"if (position.x < 0.3) {\n" +
"log = 0.7;\n" +
"firstOffset = vec2(3.0 * texelWidthOffset, 3.0 * texelHeightOffset);\n" +
"secondOffset = vec2(3.0 * texelWidthOffset, 3.0 * texelHeightOffset);\n" +
"} else {\n" +
"firstOffset = vec2(texelWidthOffset, texelHeightOffset);\n" +
"secondOffset = vec2(texelWidthOffset, texelHeightOffset);\n" +
"log = -0.1;\n" +
"}\n" +
"\n" +
"centerTextureCoordinate = inputTextureCoordinate;\n" +
"oneStepLeftTextureCoordinate = inputTextureCoordinate - firstOffset;\n" +
"twoStepsLeftTextureCoordinate = inputTextureCoordinate - secondOffset;\n" +
"oneStepRightTextureCoordinate = inputTextureCoordinate + firstOffset;\n" +
"twoStepsRightTextureCoordinate = inputTextureCoordinate + secondOffset;\n" +
"}\n";
public static final String FRAGMENT_SHADER =
"precision highp float;\n" +
"\n" +
"uniform sampler2D inputImageTexture;\n" +
"\n" +
"varying vec2 centerTextureCoordinate;\n" +
"varying vec2 oneStepLeftTextureCoordinate;\n" +
"varying vec2 twoStepsLeftTextureCoordinate;\n" +
"varying vec2 oneStepRightTextureCoordinate;\n" +
"varying vec2 twoStepsRightTextureCoordinate;\n" +
"varying float log;\n" +
"\n" +
"void main()\n" +
"{\n" +
"if (log != -0.1) {\n" +
"gl_FragColor.rgba = vec4(log, 0.0, 0.0, 1.0);\n" +
// "return;\n" +
// "}\n" +
"} else { \n" +
"lowp vec4 fragmentColor;\n" +
"fragmentColor = texture2D(inputImageTexture, centerTextureCoordinate) * 0.2;\n" +
"fragmentColor += texture2D(inputImageTexture, oneStepLeftTextureCoordinate) * 0.2;\n" +
"fragmentColor += texture2D(inputImageTexture, oneStepRightTextureCoordinate) * 0.2;\n" +
"fragmentColor += texture2D(inputImageTexture, twoStepsLeftTextureCoordinate) * 0.2;\n" +
"fragmentColor += texture2D(inputImageTexture, twoStepsRightTextureCoordinate) * 0.2;\n" +
"\n" +
"gl_FragColor = fragmentColor;\n" +
// "gl_FragColor.rgba = vec4(0.0, 1.0, 0.0, 1.0);\n" +
// "}\n" +
"}\n";
Or is there any better method to do this.
When comparing floating point values, instead of doing this :
if (log != -0.1)
You should allow a little delta/tolerance on the value to account for floating point precision and the eventual value "change" you may get from passing it as a varying.
So you should do something like :
if (abs(log - (-0.1)) >= 0.0001)
Here the 0.0001 I chosen is a bit arbitrary ... It has to be a small value ...
Another example with ==
Instead of :
if (log == 0.7)
do
if (abs(log - 0.7) <= 0.0001)
However here you probably also have another issue:
The vertex shader executes for each 3 vertex of all your triangles (or quads)
So for a specific triangle, you may set different values (-0.1 or 0.7) for log for each vertex
Now the problem is that in the fragment shader the GPU will interpolate between the 3 log values depending on which pixel it is rendering ... so in the end you can get any value in [-0.1,0.7] interval displayed on screen :-(
To avoid this kind of issue, I personally use #ifdefs in my shaders to be able to switch them between normal and debug mode, and can switch between the two with a keypress. I never try to mix normal and debug displays based on if tests, especially when the test is based on a vertex position.
So in your case I would first create a specific debug version of the shader, and then use 0.0 and 1.0 as values for log, like this what you will see are red gradients, the more red the color is, the closer you are to the case you want to test.
Related
I am using https://github.com/natario1/CameraView this library for capturing a negative image to positive and it is using the openGl shaders. I need a filter in which I can capture a negative image to positive in Black & White mode not in the normal color mode (which is currently available in the library). I tried to mix the two filters i.e first capture the negative image to positive in color mode and then apply the Black & White mode filter but as I am new to openGl, I was unable to do this. Please help me in this regard. It would be highly appreciated. The shaders which I am using are as follows :
This shader is used to convert the negative to positive in color mode.
private final static String FRAGMENT_SHADER = "#extension GL_OES_EGL_image_external : require\n"
+ "precision mediump float;\n"
+ "varying vec2 "+DEFAULT_FRAGMENT_TEXTURE_COORDINATE_NAME+";\n"
+ "uniform samplerExternalOES sTexture;\n"
+ "void main() {\n"
+ " vec4 color = texture2D(sTexture, "+DEFAULT_FRAGMENT_TEXTURE_COORDINATE_NAME+");\n"
+ " float colorR = (1.0 - color.r) / 1.0;\n"
+ " float colorG = (1.0 - color.g) / 1.0;\n"
+ " float colorB = (1.0 - color.b) / 1.0;\n"
+ " gl_FragColor = vec4(colorR, colorG, colorB, color.a);\n"
+ "}\n";
This shader is used to change the normal positive image in Black & White mode.
private final static String FRAGMENT_SHADER = "#extension GL_OES_EGL_image_external : require\n"
+ "precision mediump float;\n"
+ "varying vec2 "+DEFAULT_FRAGMENT_TEXTURE_COORDINATE_NAME+";\n"
+ "uniform samplerExternalOES sTexture;\n" + "void main() {\n"
+ " vec4 color = texture2D(sTexture, "+DEFAULT_FRAGMENT_TEXTURE_COORDINATE_NAME+");\n"
+ " float colorR = (color.r + color.g + color.b) / 3.0;\n"
+ " float colorG = (color.r + color.g + color.b) / 3.0;\n"
+ " float colorB = (color.r + color.g + color.b) / 3.0;\n"
+ " gl_FragColor = vec4(colorR, colorG, colorB, color.a);\n"
+ "}\n";
Please help in making a filter which can direct capture the negative image to positive in Black & White mode.
Thanks.
You can do that with a one-liner in a single shader:
gl_FragColor = vec4(vec3(dot(1.0 - color.rgb, vec3(1.0/3.0))), color.a);
Explanation:
the inverse color is:
vec3 inverseColor = 1.0 - color.rgb;
For the gray scale there are 2 opportunities. Either straight forward
float gray = (inverseColor.r + inverseColor.g + inverseColor.b) / 3.0;
Or by using the dot product:
float gray = dot(1.0 - inverseColor.rgb, vec3(1.0/3.0));
Finally construct a vec3 from gray:
gl_FragColor = vec4(vec3(gray), color.a);
For my android application, I want to apply brightness and contrast shader on same image.
At present I am using gpuimage plugin. In that I found two separate program for brightness and contrast as per the following.
public static final String CONTRAST_FRAGMENT_SHADER = "" +
"varying highp vec2 textureCoordinate;\n" +
" \n" +
" uniform sampler2D inputImageTexture;\n" +
" uniform lowp float contrast;\n" +
" \n" +
" void main()\n" +
" {\n" +
" lowp vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);\n" +
" \n" +
" gl_FragColor = vec4(((textureColor.rgb - vec3(0.5)) * contrast + vec3(0.5)), textureColor.w);\n" +
" }";
public static final String BRIGHTNESS_FRAGMENT_SHADER = ""
+ "varying highp vec2 textureCoordinate;\n"
+ " \n"
+ " uniform sampler2D inputImageTexture;\n"
+ " uniform lowp float brightness;\n"
+ " \n"
+ " void main()\n"
+ " {\n"
+ " lowp vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);\n"
+ " \n"
+ " gl_FragColor = vec4((textureColor.rgb + vec3(brightness)), textureColor.w);\n"
+ " }";
Now applying both of the effects I write following code
public static final String CONTRAST_BRIGHTNESS_FRAGMENT_SHADER = ""
+ "varying highp vec2 textureCoordinate;\n"
+ " uniform sampler2D inputImageTexture;\n"
+ "varying highp vec2 textureCoordinate2;\n"
+ " uniform sampler2D inputImageTexture2;\n"
+ " uniform lowp float contrast;\n"
+ " uniform lowp float brightness;\n"
+ " \n"
+ " void main()\n"
+ " {\n"
+ " lowp vec4 textureColorForContrast = texture2D(inputImageTexture, textureCoordinate);\n"
+ " \n"
+ " lowp vec4 contastVec4 = vec4(((textureColorForContrast.rgb - vec3(0.5)) * contrast + vec3(0.5)), textureColorForContrast.w);\n"
+ " lowp vec4 textureColorForBrightness = texture2D(inputImageTexture2, textureCoordinate2);\n"
+ " \n"
+ " lowp vec4 brightnessVec4 = vec4((textureColorForBrightness.rgb + vec3(brightness)), textureColorForBrightness.w);\n"
+ " gl_FragColor = contastVec4 + brightnessVec4;\n" + " }";
Doesn't able to get desire result. I can't able to figure out what I have to do next?
What program I have to write?
Why would you add the 2 results in the end? What you need to do is fetch a texel and do the 2 mathematical operations on it. Try this:
lowp vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
lowp vec3 contrastedColor = ((textureColor.rgb - vec3(0.5)) * contrast + vec3(0.5));
gl_FragColor = vec4((contrastedColor + vec3(brightness)), textureColor.w);
Still this can produce a different result then using the 2 separate shaders so let's analyse a bit what goes on:
grab a texture color
apply contrast method
write colour to buffer (at this point the colour values are clamped in range [.0, 1.0])
grab a texture color
apply brightness
write colour to buffer
So what you should add is clamping the contrast colour contrastedColor to [0,1]. As for what M. Coleman said it is true you need to consider the possibility of the result being different because of fetching multiple texels BUT in your specific case that can NOT produce a different result and you are all good to go with it. As for the blending the assumption is being either disabled or (GL_ONE, GL_ZERO) which is the same thing (some other blending types might work as well).
I have a problem with application. is a simple application atm using opengl es2.0 which running on android 2.2 device HTC sense. I can running the application on my 4.0.3 emulator as well as my device which running android 4.0.
When running on android 2.2, i have this error:
> FATAL EXCEPTION: GLThread 9
java.lang.RuntimeException: Error compiling shader:
SimpleShader.loadShader(SimpleShader.java:87)
SimpleShader.<init>(SimpleShader.java:54)
etc
I assumed that the device can run android 2.2 as the document told LINKS TO DOCUMENTATION, but not sure about the shader part though.
Here is my shader code:
String verticesShader =
"uniform mat4 uScreen;\n" +
"attribute vec2 aPosition;\n" +
"attribute vec3 aColor;\n" +
"attribute vec2 aTexPos; \n" +
"varying vec2 vTexPos; \n" +
"varying vec3 vColor;\n" +
"void main() {\n" +
" vTexPos = aTexPos; \n" +
" gl_Position = uScreen * vec4(aPosition.xy, 0.0, 1.0);\n" +
" vColor = aColor;\n" +
"}";
// Our fragment shader. Just return vColor.
// If you look at this source and just said 'WTF?', remember
// that all the attributes are defined in the VERTEX shader and
// all the 'varying' vars are considered OUTPUT of vertex shader
// and INPUT of the fragment shader. Here we just use the color
// we received and add a alpha value of 1.
String fragmentShader =
"uniform float uUseTexture; \n" +
"uniform float uAlpha; \n" +
"uniform sampler2D uTexture;\n" +
"precision mediump float;\n"+
"varying vec2 vTexPos; \n" +
"varying vec3 vColor;\n" +
"void main(void)\n" +
"{\n" +
" if ( uUseTexture != 1.0 ) \n" +
" gl_FragColor = vec4(vColor.xyz, 1); \n" +
" else \n" +
" gl_FragColor = texture2D(uTexture, vTexPos); \n" +
" gl_FragColor.a *= uAlpha;" +
//" gl_FragColor = vec4(vColor.xyz, 1);\n" +
"}";
private int loadShader(int shader, String shaderSrc) {
// TODO Auto-generated method stub
int handle = GLES20.glCreateShader(shader);
if (handle == GLES20.GL_FALSE)
throw new RuntimeException("Error creating shader!");
GLES20.glShaderSource(handle, shaderSrc);
GLES20.glCompileShader(handle);
int[] compileStatus = new int[1];
GLES20.glGetShaderiv(handle, GLES20.GL_COMPILE_STATUS, compileStatus, 0);
if (compileStatus[0] ==0)
{
String error = GLES20.glGetShaderInfoLog(handle);
GLES20.glDeleteShader(handle);
throw new RuntimeException("Error compiling shader: " + error);
}
else
return handle;
}
The application works fine on my latest device with android 4.0+
Have you considered checking your OpenGL ES GLSL version on both the android 2.2 device and the android 4.0+ device?
You can print it with GLES20.glGetString(GLES20.GL_SHADING_LANGUAGE_VERSION).
I have an android app in which I use openGL to make some neat image effects.
The base setup is that I continually draw the buffer to one of two textures, then draw on top of that texture the next frame (essentially a simulation), to give the effect that the same image keeps getting manipulated.
I'm trying to add a fade effect to this, in which I darken the background, then draw on top of that, but I'm getting a very strange artifact from this. Here is my fragment shader:
private final String mCompositeFragmentShader =
"#extension GL_OES_EGL_image_external : require\n" +
"precision mediump float;\n" +
"varying vec2 bTextureCoord;\n" +
"varying vec2 vTextureCoord;\n" +
"uniform samplerExternalOES sTexture;\n" +
"uniform sampler2D bufferTexture;\n" +
"uniform float gamma; \n" +
"void main() {\n" +
" vec4 camera = texture2D(sTexture, vTextureCoord);\n" +
" camera.r = pow(camera.r, 1.0/gamma);\n" +
" camera.g = pow(camera.g, 1.0/gamma);\n" +
" camera.b = pow(camera.b, 1.0/gamma);\n" +
" vec4 bg = texture2D(bufferTexture, bTextureCoord);\n" +
" vec3 color = max(camera.rgb, bg.rgb -vec3(.02, .02, .02));\n"+
" gl_FragColor = vec4(color,1.0);\n" +
"}\n";
with the relevant bit of code being:
vec3 color = max(camera.rgb, bg.rgb -vec3(.02, .02, .02));
So I would expect light areas to eventually fade to black over time. However, instead of black it's fading to a magenta grid:
You can see that the gridding is different for each triangle being drawn on the screen (I'm only drawing two triangles).
Here is the vertex shader, just in case:
"uniform mat4 uMVPMatrix;\n" +
"uniform mat4 uSTMatrix;\n" +
"attribute vec4 aPosition;\n" +
"attribute vec4 aTextureCoord;\n" +
"varying vec4 tempCoords;" +
"varying vec2 vTextureCoord;\n" +
"varying vec2 bTextureCoord;\n" +
"void main() {\n" +
" gl_Position = aPosition;\n" +
" bTextureCoord = aTextureCoord.xy;\n" +
" tempCoords = aTextureCoord;\n" +
" tempCoords.y = aTextureCoord.y * ("+previewSize.height+".0 /"+previewSize.width+".0);\n" +
" vTextureCoord = (uSTMatrix * tempCoords).xy;\n" +
"}\n";
Anyone have any idea what's causing this, and how to fix it?
How can I pass a float value to fragment shader ?
This is my code on android:
int aUseTexture = GLES20.glGetAttribLocation(program, "uUseTexture");
GLES20.glUniform1f(aUseTexture, 1.0f);
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
Here is my shader:
String verticesShader =
"uniform mat4 uScreen;\n" +
"attribute vec2 aPosition;\n" +
"attribute vec3 aColor;\n" +
"attribute vec2 aTexPos; \n" +
"varying vec2 vTexPos; \n" +
"varying vec3 vColor;\n" +
"void main() {\n" +
" vTexPos = aTexPos; \n" +
" gl_Position = uScreen * vec4(aPosition.xy, 0.0, 1.0);\n" +
" vColor = aColor;\n" +
"}";
// Our fragment shader. Just return vColor.
// If you look at this source and just said 'WTF?', remember
// that all the attributes are defined in the VERTEX shader and
// all the 'varying' vars are considered OUTPUT of vertex shader
// and INPUT of the fragment shader. Here we just use the color
// we received and add a alpha value of 1.
String fragmentShader =
"uniform float uUseTexture; \n" +
"uniform sampler2D uTexture;\n" +
"precision mediump float;\n"+
"varying vec2 vTexPos; \n" +
"varying vec3 vColor;\n" +
"void main(void)\n" +
"{\n" +
" if ( uUseTexture != 1.0 ) \n" +
" gl_FragColor = vec4(vColor.xyz, 1); \n" +
" else \n" +
" gl_FragColor = texture2D(uTexture, vTexPos); \n" +
//" gl_FragColor = vec4(vColor.xyz, 1);\n" +
"}";
You can see the if statement in the fragment shader , that is the one i tried to check if i pass in 1.0 it should do texture else use color.
You are probably using the wrong function call for "uniform variable". Try glGetUniformLocation() as follow:
int aUseTexture = GLES20.glGetUniformLocation(program, "uUseTexture");
Also, the floating point testing (uUseTexture != 1.0) may not be always reliable all the time. You may want to use an integer type.
As far as I know you have to pass the value through the vertex shader before it can get to the fragment shader. e.g. add "uniform float uUseTexture_in; \n" and "varying float uUseTexture; \n" at the top of the vertex shader, in the main function add "uUseTexture = uUseTexture_in;". And your shader should work