The cost of texture2D in glsl - opengl

Quite confuse about this function
In the codes of glsl, I always see something like this
uniform sampler2D source;
varying vec2 textureCordi;
void main()
{
vec2 uv = textureCordi.xy;
vec3 t1 = texture2D(source, vec2(uv.x - step_w, uv.y - step_h)).rgb; //2
float average = (t1.r + t1.b + t1.g) / 3.0;
//.....
}
In //2, the t1 save the data of the source(I think it is data), but how many data do it copy?
The coordinates of texture are between 0~1, assume that texture is an image
and the size of the image is 1024 * 768
the t1 would save 1024 * 768 number of pixels?
What would gpu do under this command?
If t1 do heavy copy job, could I ask the texture2D return
the reference of the source to t1(like c++)?

The operation looks up exactly one texel, that corresponds to the texture coordinate you passed into (taking filtering, mipmaps etc into account). The texture coordinate is the normalized coordinate of the texel you want to fetch.
Edit: t1 holds the rgb value of the one texel the operation requested (as a vector with 3 components). The normalized texture coordinate is the input into texture2D. The following line calculates the average intensity of the three channels of that one texel, not the average of the whole texture.

That is operation per fragment, and for each fragment, with texture2D you sample single texel. Many parallel operations are done on gpu for whole primitive, and in some way, yes, all data is "stored" in the end in some output buffer, but each main() functions runs only for current fragment. It is unaware what's going on with other fragments, so every operation is per-fragment.
To clarify this further more, this should help, but it might be overkill:
GPGPU - http://www.mathematik.uni-dortmund.de/~goeddeke/gpgpu/tutorial.html
Fragment shader pipeline - http://www.lighthouse3d.com/tutorials/glsl-tutorial/fragment-processor/
Hope this helps.

Related

Interpolate data buffers using OpenGL?

I am writing an application that loads the vertices of two meshes (which serve as keyframes) into two separate OpenGL float data buffers. The two meshes have the same number of vertices.
I would like to compute intermediary frames by using linear interpolation between these two buffers (where I can specify the interpolation weighting as a value between 0 and 1). I am currently performing the interpolation on the CPU, but I would like to offload the computation to the GPU in order to compute intermediary frames faster.
Is there a way to do this using only OpenGL (ie. not OpenCL)? If so, how?
Assuming that vertices are stored in the same order in both buffers, you can simply bind each of the buffers to an attribute in the vertex shader. In combination with a uniform between which controls the interpolation (let's call it t) which goes from 0 (only first buffer) to 1 (only second buffer), you can perform the linear interpolation before writing to gl_Position. A shader could look somehow like the following:
in vec4 buffer1; //Bind buffer1 to this attribute
in vec4 buffer2; //Bind buffer2 to this attribute
uniform float t; //Interpolation parameter between 0 and 1
void main()
{
gl_Position = (1 - t) * buffer1 + t * buffer2;
}

Does calculating texture Coordinates in the vertex shader increase performance in GLSL?

I recently read (for the first time) that passing an array with texture coordinates to the fragment shader for multiple lookups of a sampler is way quicker than calculating them in the fragment shader, because opengl can then prefetch these pixels. Is that true ? If so, what are the limitations ? Do I have to tell opengl that these coordinates will be used a texCoords ?
As with most performance questions, the answer is dependent on the OpenGL implementation. I know of at least one implementation which does have this optimization and one which doesn't.
In the implementations I know anything about, the prefetch happens only if you use a fragment shader input (i.e. a variable declared with varying or in) directly as an argument to a texture function. So:
in vec2 tex_coord;
main()
{
gl_FragColor = texture( diffuse_texture, tex_coord );
}
would qualify, but:
gl_FragColor = texture( diffuse_texture, 0.5 * tex_coord + 0.5 );
would not. Note that in this case the calculation could easily be done in the vertex shader. Also the compiler might be smart enough to calculate the texture coordinate early. The case most likely to hurt performance is where one set of texture coordinates is dependent on the result of another texture look-up:
vec2 tex_coord2 = texture( offset_texture, tex_coord ).rg;
gl_FragColor = texture( diffuse_texture, tex_coord2 );
Now the second texture look up can't happen until the first one completes.
You don't have to tell OpenGL that the coordinates will be used as texture coordinates.

Is a position buffer required for deferred rendering?

I'm trying to avoid the uses of a Position Buffer by projecting Screen Space Points back into View Space to use with lighting. I have tried multiplying by the inverse projection matrix, but this does not give back the View Space point. Is it worth it to add matrix multiplication to avoid the Position Buffer?
Final-pass Shader:
vec3 ScreenSpace = vec3(0.0,0.0,0.0);
ScreenSpace.xy = (texcoord.xy * 2.0) - 1.0;
ScreenSpace.z = texture2D(depthtex, texcoord.xy);
vec4 ViewSpace = InvProjectionMatrix * vec3(ScreenSpace, 1.0);
ViewSpace.xyz = ViewSpace.w;
Most of your answer can be found on this answer, which is far too long and involved to repost. However, part of your problem is that you're using texcoord and not gl_FragCoord.
You want to use gl_FragCoord, because this is guaranteed by OpenGL to be the right value (assuming your deferred pass and your lighting pass use images with the same size), no matter what. Also, it keeps you from having to pass a value from the vertex shader to the fragment shader.
The downside is that you need the size of the output screen to interpret it. But that's easy enough, assuming again that the two passes use images of the same size:
ivec2 size = textureSize(depthtex, 0);
You can use size for the size of the viewport to convert gl_FragCoord.xy into texture coordinates and window-space positions.

How to get pixel information inside a fragment shader?

In my fragment shader I can load a texture, then do this:
uniform sampler2D tex;
void main(void) {
vec4 color = texture2D(tex, gl_TexCoord[0].st);
gl_FragColor = color;
}
That sets the current pixel to color value of texture. I can modify these, etc and it works well.
But a few questions. How do I tell "which" pixel I am? For example, say I want to set pixel 100,100 (x,y) to red. Everything else to black. How do I do a :
"if currentSelf.Position() == (100,100); then color=red; else color=black?"
?
I know how to set colors, but how do I get "my" location?
Secondly, how do I get values from a neighbor pixel?
I tried this:
vec4 nextColor = texture2D(tex, gl_TexCoord[1].st);
But not clear what it is returning? if I'm pixel 100,100; how do I get the values from 101,100 or 100,101?
How do I tell "which" pixel I am?
You're not a pixel. You're a fragment. There's a reason that OpenGL calls them "Fragment shaders"; it's because they aren't pixels yet. Indeed, not only may they never become pixels (via discard or depth tests or whatever), thanks to multisampling, multiple fragments can combine to form a single pixel.
If you want to tell where your fragment shader is in window-space, use gl_FragCoord. Fragment positions are floating-point values, not integers, so you have to test with a range instead of a single "100, 100" value.
Secondly, how do I get values from a neighbor pixel?
If you're talking about the neighboring framebuffer pixel, you don't. Fragment shaders cannot arbitrarily read from the framebuffer, either in their own position or in a neighboring one.
If you're talking about accessing a neighboring texel from the one you accessed, then that's just a matter of biasing the texture coordinate you pass to texture2D. You have to get the size of the texture (since you're not using GLSL 1.30 or above, you have to manually pass this in), invert the size and either add or subtract these sizes from the S and T component of the texture coordinate.
Easy peasy.
Just compute the size of a pixel based on resolution. Then look up +1 and -1.
vec2 onePixel = vec2(1.0, 1.0) / u_textureSize;
gl_FragColor = (
texture2D(u_image, v_texCoord) +
texture2D(u_image, v_texCoord + vec2(onePixel.x, 0.0)) +
texture2D(u_image, v_texCoord + vec2(-onePixel.x, 0.0))) / 3.0;
There's a good example here

OpenGL: How to render perfect rectangular gradient?

I can render triangular gradient with simply just one triangle and using glColor for each corner.
But how to render perfect rectangular gradient? I tried with one quad, but the middle will get ugly seam. I also tried with texture of 2x2 size, it was like it should be done: proper blending from each corner, but the texture sampling precision becomes unprecise when stretched too much (i started to see pixels bigger than 1x1 size).
Is there some way of calculating this in a shader perhaps?
--
Edit: Link to images were broken(removed).
Indeed, the kind of gradient you want relies on 4 colors at each pixel, where OpenGL typically only interpolates input over triangles (so 3 inputs). Getting the perfect gradient is not possible just with the standard interpolants.
Now, as you mentioned, a 2x2 texture can do it. If you did see precision issues, I suggest switching the format of the texture to something that typically requires more precision (like a float texture).
Last, and as you mentioned also in your question, you can solve this with a shader. Say you pass an extra attribute per-vertex that corresponds to (u,v) = (0,0) (0,1) (1,0) (1,0) all the way to the pixel shader (with the vertex shader just doing a pass-through).
You can do the following in the pixel shader (note, the idea here is sound, but I did not test the code):
Vertex shader snippet:
varying vec2 uv;
attribute vec2 uvIn;
uv = uvIn;
Fragment shader:
uniform vec3 color0;
uniform vec3 color1;
varying vec2 uv;
// from wikipedia on bilinear interpolation on unit square:
// f(x,y) = f(0,0)(1-x)(1-y) + f(1,0)x(1-y) + f(0,1)(1-x)y + f(1,1) xy.
// applied here:
// gl_FragColor = color0 * ((1-x)*(1-y) + x*y) + color1*(x*(1-y) + (1-x)*y)
// gl_FragColor = color0 * (1 - x - y + 2 * x * y) + color1 * (x + y - 2 * x * y)
// after simplification:
// float temp = (x + y - 2 * x * y);
// gl_FragColor = color0 * (1-temp) + color1 * temp;
gl_FragColor = mix(color0, color1, uv.u + uv.v - 2 * uv.u * uv.v);
The problem is because you use a quad. The quad is drawn using two triangles, but the triangles are not in the orientation that you need.
If I define the quad vertices as:
A: bottom left vertex
B: bottom right vertex
C: top right vertex
D: top left vertex
I would say that the quad is composed by the following triangles:
A B D
D B C
The colors assigned to each vertex are:
A: yellow
B: red
C: yellow
D: red
Keeping in mind the geometry (the two triangles), the pixels between D and B are result of the interpolation between red and red: indeed, red!
The solution would be the a geometry with two triangles, but orientated in a different way:
A B C
A C D
But probably you will no get the exact gradient, since in middle of quad you will get a full yellow, instead of a yellow mixed with red. So, I suppose you can achieve the exact result using 4 triangles (or a triangle fan), in which the centered vertex is the interpolation between the yellow and the red.
Wooop! Effetively the result is not what I was expecting. I thought the gradient was produced by linear interpolation between colors, but surely is not (I really need to setup the LCD color space!). Indeed, the most scalable solution is rendering using fragment shaders.
Keep the solution proposed by Bahbar. I would advice to start the implementation of a pass-through vertex/fragment shader (specifying only vertices and colors you should get the previous result); then, start playing with the mix function and the texture coordinate passed to the vertex shader.
You really need to understand the rendering pipeline with programmable shaders: vertex shader is called once per vertex, fragment shader is called once per fragment (without multisampling, a fragment is a pixel; with multisampling, a a pixel is composed by a many fragments which are interpolated to get the pixel color).
The vertex shader take the input parameters (uniforms and inputs; uniforms are constant for all vertices issued between glBegin/glEnd; inputs are characteristic of each vertex shader instance (4 vertices, 4 vertex shader instances).
A fragment shader takes as input the vertex shader outputs which has produced the fragment (due the rasterization of triangles, lines and points). In the Bahbar answer the only output is the uv variable (common to both shader sources).
In you case, the vertex shader outputs the vertex texture coordinates UV (passed "as-are"). These UV coordinates are available for each fragment, and they are computed by interpolating the values outputted by the vertex shader depending on the fragment position.
Once you have those coordinates, you only need two colors: the red and the yellow in your case (in Bahbar answer corresponds to color0 and color1 uniforms). Then, mix those colors depending on the UV coordinates of the specific fragment. (*)
(*) Here is the power of shaders: you can specify different interpolation methods by simply modifying the shader source. Linear, Bilinear or Spline interpolation are implemented by specifying additional uniforms to the fragment shader.
Good practice!
Do all of your vertices have the same depth (Z) value, and are all of your triangles completely on-screen? If so, then you should have no problem getting a "perfect" color gradient over a quad made from two triangles with glColor. If not, then it's possible that your OpenGL implementation treats colors poorly.
This leads me to suspect that you may have a very old or strange OpenGL implementation. I recommend that you tell us what platform you're using, and what version of OpenGL you have...?
Without any more information, I recommend you attempt writing a shader, and avoid telling OpenGL that you want a "color." If possible, tell it that you want a "texcoord" but treat it like a color anyway. This trick has worked in some cases where color accuracy is too low.