I'm using c++, opengl 4.0 and glsh shader language.
I'm wondering how to correctly blend diffuse texture with lightmap texture.
Let's assume that we have a room. Every object has diffuse texture and lightmap. In every forum like gamedev.net or stackoverflow people say, that those textures should be multiplied. And in most cases it gives good results, but sometimes some objects are very close to light source (for example white bulb). This light source for close objects generates white lightmap. But when we multiply diffuse texture with white lightmap, then we get original diffuse texture color.
But if light source is close to some object, then color of light should be dominant
It means, that if white, strong light is close to red wall, then some part of this wall should be white, not red!
I think I need something more than just one lightmap. Lightmap don't have information about light intensity. It means, that the most shiny color is just maximum diffuse color.
Maybe I should have 2 textures - shadowmap and lightmap? Then equations should looks like this:
vec3 color = shadowmapColor * diffuseTextureColor + lightmapColor;
Is it good approach?
Generally speaking, if you're still using lightmaps, you are probably also not using HDR rendering. And without that, what you want is not particularly reasonable. Unless your light map provides the light intensity as an HDR floating-point value (perhaps in a GL_R11F_G11F_B10F or GL_RGBA16F format), this is not going to work very well.
And of course, you'll have to do the usual stuff that you do with HDR, such as tone mapping and so forth.
Lastly, your additive equation makes no sense. If the light map color represents the diffuse interaction between the light and the surface, then simply adding the light map color doesn't mean anything. The standard diffuse lighting equation is C * (dot(N, L) * I * D), where I is the light intensity, D is the distance attenuation factor, and C is the diffuse color. The value from the lightmap is presumably the parenthesized quantity. So adding it doesn't make sense.
It still needs to multiply with the surfaces's diffuse color. Any over-brightening will be due to the effective intensity of the light as a function of D.
What you need is the distance (or to save some sqrt-ing, the squared distance) of the light source to the fragment being illuminated. Then you can, in the simplest case, interpolate linearly between the light map and light source contributions:
The distance is a simple calculation which can be done per vertex in you vertex shader:
in vec4 VertexPosition; // let's assume world space for simplicity
uniform vec4 LightPosisiton; // world-space - might also be part of a uniform block etc.
out float LightDistance; // pass the distance to the fragment shader
// other stuff you need here ....
void main()
{
// do stuff
LightDistance = length(VertexPosition - LightPosisiton);
}
In your fragment shader, you use the distance to compute interpolation factors betweem light source and lightmap contributions:
in float LightDistance;
const float MAX_DISTANCE = 10.0;
uniform sampler2D LightMap;
// other stuff ...
out vec4 FragColor;
void main()
{
vec4 LightContribution;
// calculate illumination (including shadow map evaluation) here
// store in LightContribution
vec4 LightMapConstribution = texture(LightMap, /* tex coords here */);
// The following DistanceFactor will map distances in the range [0, MAX_DISTANCE] to
// [0,1]. The idea is that at LightDistance >= MAX_DISTANCE, the light source
// doesn't contribute anymore.
float DistanceFactor = min(1.0, LightDistance / MAX_DISTANCE);
// linearly interpolat between LightContribution and LightMapConstribution
vec4 FinalContribution = mix(LightContribution, LightMapConstribution, DistanceFactor);
FragColor = WhatEverColor * vec4(FinalContribution.xyz, 1.0);
}
HTH.
EDIT: To factor in Nicol Bolas' remarks, I assume that the LightMap stores the contribution encoded as an RGB color, storing the contributions for each channel. If you actually have a single channel lightmap which only store monochromatic contributions, you'll have to either use the surface color, use the color of the light source or reduce the light source contribution to a single channel.
EDIT2: Although this works mathematically, it's definitely not physically sound. You might need some correction of the final contribution to make it at least physically plausible. If your only aiming for effect, you can simply play around with correction factors until you're satisfied with the result.
Related
I'm fairly new to Shadertoy and GLSL in general. I have successfully duplicated numerous Shadertoy shaders into Blender without actually knowing how it all works. I have looked for tutorials but I'm more of a visual learner.
If someone could explain or, even better, provide some images that describe the difference between fragCoord, iResolution, & fragColor. That would be great!
I'm mainly interested in the Numbers. Because I use Blender I'm used to the canvas being 0 to 1 -or- -1 to 1
This one in particular has me a bit confused.
vec2 u = (fragCoord - iResolution.xy * .5) / iResolution.y * 8.;
I can't reproduce the remaining code in Blender without knowing the coordinate system.
Any help would be greatly appreciated!
It is normal, you cannot reproduce this code in blender without knowing the coordinate system.
The Shadertoy documentation states:
Image shaders implement the mainImage() function to generate
procedural images by calculating a color for each pixel in the image.
This function is invoked once in each pixel and the host application
must provide the appropriate input data and retrieve the output color
to assign it to the corresponding pixel on the screen. The signature
of this function is:
void mainImage( out vec4 fragColor, in vec2 fragCoord);
where fragCoord contains the coordinates of the pixel for which the
shader must calculate a color. These coordinates are counted in pixels
with values from 0.5 to resolution-0.5 over the entire rendering
surface and the resolution of this surface is transmitted to the
shader via the uniform iResolution variable.
Let me explain.
The iResolution variable is a uniform vec3 which contains the dimensions of the window and is sent to the shader with some openGL code.
The fragCoord variable is a built-in variable that contains the coordinates of the pixel where the shader is being applied.
More concretely:
fragCoord : is a vec2 that is between 0 > 640 on the X axis and 0 > 360 on the Y axis
iResolution : is a vec2 with an X value of 640 and a Y value of 360
quick note on how vectors work in OpenGL:
if you have also a hard time understanging how vector work in OpenGL, I highly recommand to read the anwser of Homan bellow, a very usefull introduction to OpenGL swizzling.
This image was calculated with the following code:
// Normalized pixel coordinates (between 0 and 1)
vec2 uv = fragCoord/iResolution.xy;
// Set R and G values based on position
vec3 col = vec3(uv.x,uv.y,0);
// Output to screen
fragColor = vec4(col,1.0);
The output ranges from 0,0 in the lower-left and 1,1 in the upper-right. This is the default lower-left windows space set by OpenGL.
This an image was calculated with the following code:
// Normalized pixel coordinates (between -0.5 and 0.5)
vec2 uv = (fragCoord - iResolution.xy * 0.5)/iResolution.xy;
// Set R and G values based on position
vec3 col = vec3(uv.x,uv.y,0);
// Output to screen
fragColor = vec4(col,1.0);
The output ranges from -0.5,-0.5 in the lower-left and 0.5,0.5 because
in the first step we subtract half of the window size [0.5] from each pixel coordinate [fragCoord]. You can see the effect in the way the red and green values don't kick into visibility until much later.
You might also want to normalize only the y axis by changing the first step to
vec2 uv = (fragCoord - iResolution.xy * 0.5)/iResolution.y;
Depending our your purpose the image can seem strange if you normalize both axes so this is a possible strategy.
This an image was calculated with the following code:
// Normalized pixel coordinates (between -0.5 to 0.5)
vec2 uv = (fragCoord - iResolution.xy * 0.5)/iResolution.xy;
// Set R and G values based on position using ceil() function
// The ceil() function returns the smallest integer that is greater than the uv value
vec3 col = vec3(ceil(uv.x),ceil(uv.y),0);
// Output to screen
fragColor = vec4(col,1.0);
The ceil() function allows us to see that the center of the image is 0, 0
As for the second part of the shadertoy documentation:
The output color is returned in fragColor as a four-component vector,
the last component being ignored by the client. The result is
retrieved in an "out" variable in anticipation of the future addition
of several rendering targets.
Really all this means is that fragColor contains four values that are shopped to the next stage in the rendering pipeline. You can find more about in and out variables here.
The values in fragColor determine the color of the pixel where the shader is being applied.
If you want to learn more about shaders these are some good starting places:
the book of shader - uniform
learn OpenGL - shader
Not to take away from the accepted answer, which is very thorough. But in case anyone else was confused about the types, iResolution is a 'uniform highp 3-component vector of float'... so actually a vec3? That's why we see in examples that fragCoord (actually a vec2) is divided by iResolution.xy (the .xy gives us a vec2). But what is the '.xy' thing? Is it a method? An attribute or property? With some random googling I found out that the '.xy' tacked on at the end is called 'swizzling'
https://www.khronos.org/opengl/wiki/Data_Type_(GLSL)#Vectors
(for convenience the gist of it is here below)
Swizzling
You can access the components of vectors using the following syntax:
vec4 someVec;
someVec.x + someVec.y;
This is called swizzling. You can use x, y, z, or w, referring to the
first, second, third, and fourth components, respectively.
The reason it has that name "swizzling" is because the following syntax is entirely valid:
vec2 someVec;
vec4 otherVec = someVec.xyxx;
vec3 thirdVec = otherVec.zyy;
You can use any combination of up to 4 of the letters to create a vector (of the same basic type) of that length. So otherVec.zyy is a vec3, which is how we can initialize a vec3 value with it. Any combination of up to 4 letters is acceptable, so long as the source vector actually has those components. Attempting to access the 'w' component of a vec3 for example is a compile-time error.
Swizzling also works on l-values (left values?):
vec4 someVec;
someVec.wzyx = vec4(1.0, 2.0, 3.0, 4.0); // Reverses the order.
someVec.zx = vec2(3.0, 5.0); // Sets the 3rd component of someVec to 3.0 and the 1st component to 5.0
However, when you use a swizzle as a way of setting component values, you cannot use the same swizzle component twice. So someVec.xx = vec2(4.0, 4.0); is not allowed.
Additionally, there are 3 sets of swizzle masks. You can use xyzw, rgba (for colors), or stpq (for texture coordinates). These three sets have no actual difference; they're just syntactic sugar. You cannot combine names from different sets in a single swizzle operation. So ".xrs" is not a valid swizzle mask.
In OpenGL 4.2 or ARB_shading_language_420pack, scalars can be swizzled as well. They obviously only have one source component, but it is legal to do this:
float aFloat;
vec4 someVec = aFloat.xxxx;
// -1 to 1
vec2 uv = (2.0 * fragCoord - iResolution.xy) / iResolution.xy;
vec3 col = vec3(uv.x, uv.y, 0.0);
fragColor = vec4(col1, 1.0);
I'm trying to render a screen. So far, I have intersected my rays with the objects in the scene and if there is an intersection, I set a random color to that intersection.
What I need to do next is color pixels according to their values. I have watched more than 10 tutorials, read several websites about coloring the pixels. However, the file I'm reading does not have colors of the objects. Instead it has the following:
An ambient light, with rgb color
A point light, with rgb color and position
Objects have ambient, diffuse, specular, mirror reflectance(in rgb) and a phong exponent(a value).
Also, I know that the intensity of the light emitted is proportional to the square of the distance(as distance becomes larger, there is less light on an object).
If I had the color of the object, I could use the algorithm below:
Color3 trace(..)
{
...
Color3 ambient = object.color * 0.3;
Color3 phong = phongModel(..) or object.color;
Color3 reflection = trace(..);
return ambient + phong + reflection;
}
as in stated in: How to compute reflected color?
I don't have the color of the object, just reflectance values. How can I calculate the color of the object?
However, the file I'm reading does not have colors of the objects.
Instead it has the following:
...
Objects have ambient, diffuse, specular, mirror reflectance(in rgb) and a phong exponent(a value).
If you have RGB far all these, that is the color of the object.
Multiply incoming light with how the object reflects it in given angles and the result is the color by which it is perceived.
I've written the following shader to perform a bright pass of my scene so I can extract luminance for later blurring as part of a "glow" effect.
// "Bright" pixel shader.
#version 420
uniform sampler2D Map_Diffuse;
uniform float uniform_Threshold;
in vec2 attrib_Fragment_Texture;
out vec4 Out_Colour;
void main(void)
{
vec3 luminances = vec3(0.2126, 0.7152, 0.0722);
vec4 texel = texture2D(Map_Diffuse, attrib_Fragment_Texture);
float luminance = dot(luminances, texel.rgb);
luminance = max(0.0, luminance - uniform_Threshold);
texel.rgb *= sign(luminance);
texel.a = 1.0;
Out_Colour = texel;
}
The bright areas are successfully extracted however there are some unstable features in the scene sometimes, resulting in pixels that flicker on and off for a while. When this is blurred the effect is more pronounced, with bits of glow kind-of flickering too. The artifacts occur in, for example, the third image in the screenshot I've posted, where the object is in shadow and so there's far less luminance in the scene. They're mostly present in transition from away to towards the light of course (during rotation of the object), where the edge is just hitting the light.
My question is to ask whether there's a way you can detect and mitigate this in the shader. Note that the bright pass is part of a general down-sample, from screen resolution to 512x512.
You could read the surrounding pixels also and do your math based on that.
Kind of like is done here.
I'm trying to implement a ground fog shader for my terrain rendering engine.
The technique is described in this article: http://www.iquilezles.org/www/articles/fog/fog.htm
The idea is to consider the ray going from the camera to the fragment and integrate the fog density function along this ray.
Here's my shader code:
#version 330 core
in vec2 UV;
in vec3 posw;
out vec3 color;
uniform sampler2D tex;
uniform vec3 ambientLightColor;
uniform vec3 camPos;
const vec3 FogBaseColor = vec3(1., 1., 1.);
void main()
{
vec3 light = ambientLightColor;
vec TexBaseColor = texture(tex,UV).rgb;
//***************************FOG********************************************
vec3 camFrag = posw - camPos;
float distance = length(camFrag);
float a = 0.02;
float b = 0.01;
float fogAmount = a * exp(-camPos.z*b) * ( 1.0-exp( -distance*camFrag.z*b ) ) / (b*camFrag.z);
color = mix( light*TexBaseColor, light*FogBaseColor, fogAmount );
}
The first thing is that I don't understand how to choose a and b and what are their physical role in the fog density function.
Then, the result is not what I expect…
I have a ground fog but the transition of fogAmount from 0 to 1 is always centered at the camera altitude. I've tried a lot of different a and b but when I don't have a transition at camera altitude, I either have a full fogged or not fogged at all terrain.
I checked the data I use and everything's correct:
camPos.z is the altitude of my camera
camFrag.z is the vertical component of the vector going from the camera to the fragment
I can't get to understand what part of the equation cause this.
Any idea about this ?
EDIT : Here's the effect I'm looking for :
image1
image2
This is a pretty standard application of atmospheric scattering.
It is discussed under the umbrella of volumetric lighting usually, which involves transmittance of light through different media (e.g. smoke, air, water). In cutting-edge shader-based graphics, this can be achieved in real-time using ray-marching or if there is only one uniform participating medium (as it is in this case -- the fog only applies to air), simplified to integration over some distance.
Ordinarily you would ray-march through participating media in order to determine the properties of light transfer, but this application is simplified to assume a medium that has well-defined distribution characteristics and that is where the coefficients you are confused about come from. The density of fog varies exponentially with distance, and this is what b is controlling, likewise it also varies with altitude (not shown in the equation directly below).
(source: iquilezles.org)
What this article introduces to the discussion, however, are poorly named coefficients a and b. These control in-scattering and extinction. The author repeatedly refers to the extinction coefficient as extintion, which really makes no sense to me - hopefully this is just because English was not the author's native language. Extinction can be thought of as how quickly light is absorbed, and it describes the opacity of a medium. If you want a more theoretical basis for all of this, have a look at the following paper.
With this in mind, take another look at the code from your article:
vec3 applyFog( in vec3 rgb, // original color of the pixel
in float distance, // camera to point distance
in vec3 rayOri, // camera position
in vec3 rayDir ) // camera to point vector
{
float fogAmount = c*exp(-rayOri.y*b)*(1.0-exp(-distance*rayDir.y*b))/rayDir.y;
vec3 fogColor = vec3(0.5,0.6,0.7);
return mix( rgb, fogColor, fogAmount );
}
You can see that c in this code actually a from the original equation.
More importantly, there is an additional expression here:
This additional expression controls the density with respect to altitude. Judging by your implementation of the shader, you have not correctly implemented the second expression. camFrag.z is very likely not altitude, but rather depth. Furthermore, I do not understand why you are multiplying it by the b coefficient.
I found a method that gives the result I was looking for.
The method is described in this article of Eric Lengyel: http://www.terathon.com/lengyel/Lengyel-UnifiedFog.pdf
It explains how to create a fog layer with density and altitude parameters. You can fly through it, it progressively blends all the geometry above the fog.
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