I am trying to render a circle on my mobile uisng farment shader. Also followed this as i got the best answer.
Vertex Shader:
attribute vec4 position;
attribute vec4 inputTextureCoordinate;
varying vec2 textureCoordinate;
void main()
{
gl_Position = position;
textureCoordinate = inputTextureCoordinate.xy;
}
Fragment Shader:
varying highp vec2 textureCoordinate;
const highp vec2 center = vec2(0.5, 0.5);
const highp float radius = 0.5;
void main()
{
highp float distanceFromCenter = distance(center, textureCoordinate);
lowp float checkForPresenceWithinCircle = step(distanceFromCenter, radius);
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0) * checkForPresenceWithinCircle;
}
attribute vec4 position; is passed -1 to +1
and
attribute vec4 inputTextureCoordinate; is passed 0 to 1.
But while rendering I get a ellipse on Mobile Screen? I think this might be because of the screen aspect ratio. How to render perfect circle on screen?
I think this might be because of the screen aspect ratio.
Yes, this is exactly the problem.
The viewinf voulme iis [-1,1] in all 3 dimensions. That is mapped to the viewport for window space coordinates. Since you do not use any other transformations, you are direcly drawin in clip space, and your clip space is identical to the NDC space.
To get this right, you have to take the aspect ratio into account. You can either directly change your attribute values, or correct for it in the vertex shader, or still draw the full-screen quad and take account for it in the fragment shader.
The latter one would be the most inefficient way. I would actually recommend adding a 2D scale vector uniform to the vertex shader.
attribute vec4 position;
attribute vec4 inputTextureCoordinate;
varying vec2 textureCoordinate;
uniform vec2 scale;
void main()
{
gl_Position = vec4(scale, 1.0, 1.0) * position;
textureCoordinate = inputTextureCoordinate.xy;
}
On your client side, you can set the uniform to (1.0/aspect_ratio, 1.0) if aspect_ratio is >= 1.0, and to (1.0, aspect_ratio) if it is below 1. That way, no matter what screen orientation you use, the circle will always be a circle and fit to the screen.
Related
Android OpenGL ES 2.0, I bind bitmap and depth map bitmap to fragment_shader.glsl,
precision mediump float;
uniform sampler2D sDepth;
uniform sampler2D sTexture;
uniform float time;
varying vec2 varyTexCoord;
void main() {
vec4 depth=texture2D(sDepth, varyTexCoord);
gl_FragColor=texture2D(sTexture, varyTexCoord);
}
sTexture : original bitmap
sDepth: depth map(ARGB_8888, 0~255, 0:far 255:near)
vertex_shader.glsl
attribute vec4 vPosition;
attribute vec2 vTexCoord;
uniform mat4 vMatrix;
varying vec2 varyTexCoord;
void main() {
gl_Position = vPosition;
varyTexCoord = vTexCoord;
}
now I want to make parallax effect :According to the depth of field value, zoom in the image, the zoom in of the near area is higher than that of the far area, creating parallax effect.
Can you give us some ideas? Thank you.
Create a mesh on a base of regular grid, let say 10x10 in (x,y) space.
Set z coordinate from depth map.
Make uv coordinates.
Render mesh with you color texture, and use gl_position = projectionMatrix * scale * vertex;
Try different dimensions for grid to find best one.
I use Vuforia SDK to render the video stream of my phone's camera on the screen.
So the texture is generated by the Vuforia library, not me.
The shaders used to render this background are:
// Vertex Shader
attribute vec4 a_position;
attribute vec2 a_textureCoords;
varying vec2 v_textureCoords;
uniform mat4 u_projectionMatrix;
void main()
{
gl_Position = u_projectionMatrix * a_position;
v_textureCoords = a_textureCoords;
}
// Fragment Shader
varying highp vec2 v_textureCoords;
uniform sampler2D u_currentTexture;
void main()
{
vec4 currentColor = texture2D(u_currentTexture, v_textureCoords);
gl_FragColor = currentColor;
}
Now, I want an overlay in the upper-left corner of the screen:
I don't want this overlay to display only a pink texture, but rather a multiply blend of the pink texture and the background texture. Note that the textures do not have the same coordinates.
But for now, let's forget about the blending and let's just render the background texture in the shader program of the pink texture. So in the end, yes, one should see no difference between the background-only version and the bacground with overlay version.
As you can see (look at the painting and the top of the chair), there is a small offset...
The shaders used to render the overlay are:
// Vertex Shader
attribute vec4 a_position;
attribute vec2 a_currentTextureCoords;
varying vec2 v_currentTextureCoords;
void main()
{
gl_Position = a_position;
v_currentTextureCoords = a_currentTextureCoords;
}
// Fragment Shader
varying highp vec2 v_currentTextureCoords;
uniform sampler2D u_currentTexture;
uniform sampler2D u_backgroundTexture;
void main()
{
vec2 screenSize = vec2(1080.0, 1920.0);
vec2 cameraResolution = vec2(720.0, 1280.0);
vec2 texelSize = vec2(1.0 / screenSize.x, 1.0 / screenSize.y);
vec2 scaleFactor = vec2(cameraResolution.x / screenSize.x, cameraResolution.y / screenSize.y);
vec2 uv = gl_FragCoord.xy * texelSize * scaleFactor;
uv = vec2(scaleFactor.y - uv.y, scaleFactor.x - uv.x);
vec4 backgroundColor = texture2D(u_backgroundTexture, uv);
gl_FragColor = backgroundColor;
}
Are my calculations wrong?
Why do you need this line?
uv = vec2(scaleFactor.y - uv.y, scaleFactor.x - uv.x);
Not sure what arithmetic relationship the absolute texture coordinates have with the scale factor which needs an addition or a subtraction ...
P.S. it's not related to your question, but your shaders will be shorter and easier to read if you just use the vector operations in the language. For example, replace:
vec2 scaleFactor = vec2(cameraResolution.x / screenSize.x, cameraResolution.y / screenSize.y);
... with ...
vec2 scaleFactor = cameraResolution / screenSize;
As long as the vector types are the same length, it will do exactly what you expect with a lot less typing ...
I try to develop an android application that swap the pixels (of the camera preview) bottom <=> top.
swap diagram
So I wrote this vertex shader but it displays very strange things (a black screen with digital noise).
attribute vec4 vPosition;
attribute vec2 inputTextureCoordinate;
varying vec2 textureCoordinate;
void main()
{
if (vPosition.y > 0.0)
{
gl_Position = vPosition - vec4(0.0, 1.0, 0.0, 0.0);
}
else
{
gl_Position = vPosition + vec4(0.0, 1.0, 0.0, 0.0);
}
textureCoordinate = inputTextureCoordinate;
}
But this vertex shader below works, but it only moves top pixels to bottom.
attribute vec4 vPosition;
attribute vec2 inputTextureCoordinate;
varying vec2 textureCoordinate;
void main()
{
if (vPosition.y > 0.0)
{
gl_Position = vPosition - vec4(0.0, 1.0, 0.0, 0.0);
}
else
{
gl_Position = vPosition;
}
textureCoordinate = inputTextureCoordinate;
}
Do you know how to swap the pixels as in my diagram (make my first vertex shader work)?
Thank you
Avoid if statement in shaders performance may deteriorate.
Simple solution may be to swap texture coordinates in fragment shader. I suppose your v texture coordinates are going from 0 to 1 along whole height of the quad where camera image is rendered. Then following code in fragment shader should do the trick.
textureCoordinate = vec2(textureCoordinate.u, 1 - textureCoordinate.v);
But, the actual texture coordinate swap depends on way you define your texture coordinates at the quad's vertices.
I have written this simple shader to overlay texture over another (base) texture -
varying highp vec2 textureCoordinate;
varying highp vec2 textureCoordinate2;
uniform sampler2D inputImageTexture;
uniform sampler2D inputImageTexture2;
void main()
{
mediump vec4 base = texture2D(inputImageTexture, textureCoordinate);
mediump vec4 overlay = texture2D(inputImageTexture2, textureCoordinate2);
mediump float ra = (overlay.a) * overlay.r + (1.0 - overlay.a) * base.r;
mediump float ga = (overlay.a) * overlay.g + (1.0 - overlay.a) * base.g;
mediump float ba = (overlay.a) * overlay.b + (1.0 - overlay.a) * base.b;
gl_FragColor = vec4(ra, ga, ba, 1.0);
}
Issue - This works except for one issue. If the overlay image is smaller than the base image, the outside region of overlay image gives alpha value of 1.0, i.e overlay.a == 1.0. Due to this the base image is clipped by overlay image. The region outside overlay appears as black.
I am new to opengl, and was expecting that outside its bounds, the texture's alpha should appear as 0.0? How can I fix my shader code to achieve desired behaviour? Or do I need to modify my graphics pipeline?
EDIT Vertex shader below-
attribute vec4 inputTextureCoordinate2;
varying vec2 textureCoordinate;
varying vec2 textureCoordinate2;
void main()
{
gl_Position = pos;
textureCoordinate = uv;
textureCoordinate2 = inputTextureCoordinate2.xy;
}
I was expecting that outside its bounds, the texture's alpha should appear as 0.0
How are you sampling the texture outside of its bounds? When sampling a texture, the uv coordinates should range from 0 to 1. If the coordinates are outside of this range, then one of two things will happen:
If GL_CLAMP_TO_EDGE is set, then the cooridnate will be clamped to the (0, 1) range, and you'll sample an edge pixel
If GL_REPEAT is set, then the fractional part of the coordinate will be taken, and you'll sample somewhere in the middle of the texture
See the docs on glTexParameter for more details.
If your use case is simply overlaying images, perhaps you should try writing a pixel shader.
Set the viewport to the base image dimensions and draw a quad from (-1, 1).
Your fragment shader will now operate on each pixel, known as a texel. Get the texel with gl_FragCoord
Sample the base and overlay by texel e.g. using texelFetch
If the texel is outside of the overlay, set the overlay's rgba values to 0
For example
//fragment shader
uniform ivec2 overlayDim;
uniform sampler2D baseTexture;
uniform sampler2D overlayTexture;
void main() {
vec2 texelf = floor(gl_FragCoord.xy);
ivec2 texel = (int(texelf.x), int(texelf.y));
vec4 base = texelFetch(baseTexture, texel, 0);
vec4 overlay = texelFetch(overlayTexture, texel, 0);
float overlayIsValid = float(texel.x < overlayDim.x && texel.y < overlayDim.y);
overlay *= overlayIsValid;
//rest of code
}
What happens if you sample outside the range of the texture is controlled by the value you set for GL_TEXTURE_WRAP_S and GL_TEXTURE_WRAP_T using glTexParameteri().
In full OpenGL, you could set the value to GL_CLAMP_TO_BORDER, set the border color to a value with alpha 0.0, and be done with it. But texture borders are not available in OpenGL ES 2.0 (the option is introduced in ES 3.2, but not in earlier versions).
Without this, I can think of two options:
If you have control over the texture data, you could set a one pixel border to transparent values. The GL_CLAMP_TO_EDGE then gives you a transparent value when sampling outside the range.
Check the range in the fragment shader.
The fragment shader code for the second option could look something like this (untested):
mediump vec3 col = texture2D(inputImageTexture, textureCoordinate).xyz;
if (all(greaterThan(textureCoordinate2, vec2(0.0))) &&
all(lessThan(textureCoordinate2, vec2(1.0))))
{
mediump vec3 overlay = texture2D(inputImageTexture2, textureCoordinate2).xyz;
col = mix(col, overlay, overlay.a);
}
gl_FragColor = vec4(col, 1.0);
Compared to your original code, also note the use of vector operations. Whenever there is a good way of operating on vectors, it will make the code simpler. It will also make the job of the optimizer easier for GPUs with vector operations.
I found the issue in my code. I had
GLES20.glClearColor(0, 0, 0, 1);
in my code. Changing it to -
GLES20.glClearColor(0, 0, 0, 0);
fixed the issue.
Also as mentioned by #Reto, I have changed my fragment shader to use vector operations for optimisation.
void main()
{
mediump vec4 overlay = texture2D(inputImageTexture2, textureCoordinate2);
mediump vec3 col = texture2D(inputImageTexture, textureCoordinate).xyz;
col = mix(col, overlay.xyz, overlay.a);
gl_FragColor = vec4(col, 1.0);
}
I needed to create some ripling effect for one sprite in my game, here's the vertexShader:
attribute vec4 a_position; // just taking in necessary attributes
attribute vec2 a_texCoord0;
uniform mat4 u_projTrans; // Combination of view and projection matrix
varying vec2 v_texCoords;
void main() {
v_texCoords = a_texCoord0;
gl_Position = u_projTrans * a_position; //as I said, it is sprite so no need for modelMatrix
}
and here's the fragment:
#ifdef GL_ES
precision mediump float;
#endif
varying vec2 v_texCoords;
uniform sampler2D u_texture; //texture of sprite
uniform float time;
void main()
{
vec2 uv;
if (time > 0.0) { // time is > 0.0 when I want the ripling effect to be applied,
vec2 cPos = -1.0 + 2.0 * v_texCoords.xy; // converting tex.Coords to -1 - 1
float cLength = length(cPos); //taking length of it
uv = v_texCoords.xy +( (cPos/cLength)*cos(cLength*12.0-time*4.0)*0.03 ) // just some calculations for the ripling effect
}
else
uv = v_texCoords.xy; // if I don't want to use the ripling effect, I use normal texCoords
vec4 tex = texture2D(u_texture, uv); //sampling texture
gl_FragColor = tex;
}
It all works fine, the performance's fine on PC, but when running it on android, the performance is a lot worse... As you can see, shader's are trivial but they somehow are expensive.. Anyways, sprite I draw has width about 2000 - 4000 px and height 720. Also, when I replace v_texCoords with different vector(for example vec2(1, 1)) in cPos calc: vec2 cPos = -1.0 + 2.0 * v_texCoords.xy; the performance improves heavily..
I don't really know what's so expensive there. If anyone had some advices, I'd be happy. Thanks in advance