Diffuse lighting for a moving object - java

When calculating diffuse lighting for a moving object, I have to move the luminous source along with the object itself:
#Override
public void draw() { // draw frame
...
// Move object
GLES20.glVertexAttribPointer(aPositionLink, 3, GLES30.GL_FLOAT,
false, 0, object3D.getVertices());
// The luminous source moves nearby the object, so the
// object is always illuminated from one side
GLES20.glUniform3f(lightPositionLink, object3D.getX(),
object3D.getY(), object3D.getZ() + 2.0f);
...
}
Snippet of vertex shader:
#version 300 es
uniform mat4 u_mvMatrix; // model-view matrix of object
uniform vec3 u_lightPosition; // position of the luminous source
in vec4 a_position; // vertex data is loaded here
in vec3 a_normal; // normal data is loaded here
struct DiffuseLight {
vec3 color;
float intensity;
};
uniform DiffuseLight u_diffuseLight;
...
void main() {
...
vec3 modelViewNormal = vec3(u_mvMatrix * vec4(a_normal, 0.0));
vec3 modelViewVertex = vec3(u_mvMatrix * a_position);
// calculate the light vector by subtracting the
// position of the object from the light position
vec3 lightVector = normalize(u_lightPosition - modelViewVertex);
float diffuse = max(dot(modelViewNormal, lightVector), 0.1);
float distance = length(u_lightPosition - modelViewVertex);
diffuse = diffuse * (1.0 / (1.0 + pow(distance, 2.0)));
// calculate the final color for diffuse lighting
lowp vec3 diffuseColor = diffuse * u_diffuseLight.color * u_diffuseLight.intensity;
v_commonLight = vec4((ambientColor + diffuseColor), 1.0);
...
}
Is this the right approach? Or is there another rational option with stationary luminous source so as not to expend resources on calculating the position of the luminous source each frame? Note: Increasing the distance does not help. Thanks in advance.
SOLUTION:
On the advice of Rabbid76 I applied directional light as described here.

I have to move the luminous source along with the object itself
Why does the light source move with the object?
If the light is a point light source in the world, and the object moves, then the illumination of the object changes (in the "real" world).
In your case, the lighting is computed in view space. If the light source is a point in the world, then you have to transform the position by the view matrix (the view matrix transforms from world space to view space). e.g:
uniform mat4 u_viewMatrix;
void main()
{
// [...]
vec3 lightPosView = vec3(u_viewMatrix * vec4(u_lightPosition.xyz, 1.0));
vec3 lightVector = normalize(u_lightPosition - modelViewVertex);
// [...]
}
Anyway, if the object moves and the light source is somehow anchored to the object, the you have to apply the transformations, which are applied to the vertices of the object, to the light source, too.
In that case u_lightPosition has to be a position in the model space of the object, that means it is relative to the object (u_lightModelPosition). Then you can do:
uniform vec3 u_lightModelPosition;
void main()
{
mat3 normalMat = inverse(transpose(mat3(u_mvMatrix)));
vec3 modelViewNormal = normalMat * a_normal;
vec3 modelViewVertex = vec3(u_mvMatrix * a_position);
vec3 modelViewLight = vec3(u_mvMatrix * vec4(u_lightModelPosition, 1.0));
vec3 lightVector = normalize(modelViewLight - modelViewVertex);
// [...]
}
If you want a light, that doesn't depend on the position, the you have to use a directional light. In that case the light source is not a point in the world, it is a direction only. e.g.:
vec3 lightVector = -u_lightRayDirection;
u_lightRayDirection has to be in the space of the light calculations. Since the lighting is computed in view space, u_lightRayDirection has to be a direction in view space, too. If u_lightRayDirection is a vector in world space, then it has to be transformed by mat3(u_viewMatrix).
A directional light has no distance (or a constant distance).
If the light source is anchored to the camera, no transformations are required at all (because you the light calculations in view space).

Related

Why does sprite size affect performance in OpenGL ES 2.0/3.0?

There is a particle system for an explosion:
Vertex shader:
#version 300 es
uniform float u_lastTimeExplosion; // time elapsed since the explosion
// explosion center (particle coordinates are set relative to this center
uniform vec3 u_centerPosition;
uniform float u_sizeSprite;
layout(location = 0) in float a_lifeTime; // particle lifetime in seconds
// initial position of the particle at the time of the explosion
layout(location = 1) in vec3 a_startPosition;
layout(location = 2) in vec3 a_endPosition; // final position of the particle
out float v_lifeTime; // remaining particle lifetime
void main()
{
gl_Position.xyz = a_startPosition + (u_lastTimeExplosion * a_endPosition);
gl_Position.xyz += u_centerPosition;
gl_Position.w = 1.0;
// calculate the remaining particle lifetime
v_lifeTime = 1.0 - (u_lastTimeExplosion / a_lifeTime);
v_lifeTime = clamp(v_lifeTime, 0.0, 1.0);
// calculate sprite size based on remaining life time
gl_PointSize = pow(v_lifeTime, 5.0) * u_sizeSprite;
}
Fragment shader:
#version 300 es
precision lowp float;
in float v_lifeTime;
uniform vec4 u_color;
out vec4 fragColor;
uniform sampler2D s_texture;
void main()
{
vec4 texColor = texture(s_texture, gl_PointCoord);
fragColor = u_color * texColor;
fragColor.a *= v_lifeTime; // increase sprite transparency
}
If the size of the sprite is less than 10, then everything is fine:
GLES20.glUniform1f(sizeSpriteLink, 10f);
If the size of the sprite increases, then is a slowdown in rendering (FPS reduction):
GLES20.glUniform1f(sizeSpriteLink, 150f);
Strangely - the number of sprites affects performance less than their size.
Question: Why does sprite size affect performance? Would be grateful for any anwer/comment.
Note: mipmap for particle texture used.
Not sure, but it seems that increasing the size of the sprite greatly affects the consumption of graphic memory resources. Maybe someone knows about this?
It is also better to use this type of filtering to increase performance:
// one value is taken from the nearest pyramid level
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER,
GLES20.GL_NEAREST_MIPMAP_NEAREST);

Rendering a laser beam - how to make it face camera?

(I am using a LibGDX framework which is basically just LWJGL(Java) with OpenGL for rendering)
Hi, I'm trying to render a laser beam, so far I've got this effect,
It's just a rectangle and then the whole effect is done in fragment Shader.
However, as it is a laser beam, I want the rectangle to face a camera, so the player always sees this red transparent "line" everytime. And this is driving me crazy. I tried to do some billboarding stuff, however what I want isn't really billboarding. I just want to rotate it on Z axis so that the player always sees the whole line, that's all. No X and Y rotations.
As you can see, that's what I want. And it's not billboarding at all.
If it was billboarding, it would look like this: .
I also tried to draw cylinder and the effect based on gl_FragCoord, which was working fine, but the coords were varying(sometimes the UVs were 0 and 1, sometimes 0 and 0.7) and it was not sampling whole texture, so the effect was broken.
Thus I don't even know what to do now.
I would really appreciate any help. Thanks in advance.
Here's vertexShader code:
attribute vec3 a_position;
attribute vec2 a_texCoord0;
uniform mat4 u_worldTrans; //model matrix
uniform mat4 u_view; //view matrix
uniform mat4 u_proj; // projection matrix
varying vec2 v_texCoord0;
void main() {
v_texCoord0 = a_texCoord0;
vec4 worldTrans = u_worldTrans * vec4(a_position, 1.0);
gl_Position = u_proj * u_view * worldTrans;
}
and here's fragmentShader codE:
#ifdef GL_ES
precision mediump float;
#endif
varying vec2 v_texCoord0;
uniform sampler2D tex; //texture I apply the red color onto. It's how I get the smooth(transparent) edges.
void main() {
vec4 texelColor = texture2D( tex, v_texCoord0 ); //sampling the texture
vec4 color = vec4(10.0,0.0,0.0,1.0); //the red color
float r = 0.15; //here I want to make the whole texture be red, so when there's less transparency, I want it to be more red, and on the edges(more transparency) less red.
if (texelColor.a > 0.5) r = 0.1;
gl_FragColor = vec4(mix(color.rgb,texelColor.rgb,texelColor.a * r),texelColor.a); //and here I just mix the two colors into one, depengind on the alpha value of texColor and the r float.
}
The texture is just a white line opaque in the middle, but transparent at the edges of the texuture. (smooth transition)
If you use DecalBatch to draw your laser, you can do it this way. It's called axial billboarding or cylindrical billboarding, as opposed to the spherical billboarding you described.
The basic idea is that you calculate the direction the sprite would be oriented for spherical billboarding, and then you do a couple of cross products to get the component of that direction that is perpendicular to the axis.
Let's assume your laser sprite is aligned to point up and down. You would do this series of calculations on every frame that the camera or laser moves.
//reusable calculation vectors
final Vector3 axis = new Vector3();
final Vector3 look = new Vector3();
final Vector3 tmp = new Vector3();
void orientLaserDecal (Decal decal, float beamWidth, Vector3 endA, Vector3 endB, Camera camera) {
axis.set(endB).sub(endA); //the axis direction
decal.setDimensions(beamWidth, axis.len());
axis.scl(0.5f);
tmp.set(endA).add(axis); //the center point of the laser
decal.setPosition(tmp);
look.set(camera.position).sub(tmp); //Laser center to camera. This is
//the look vector you'd use if doing spherical billboarding, so it needs
//to be adjusted.
tmp.set(axis).crs(look); //Axis cross look gives you the
//right vector, the direction the right edge of the sprite should be
//pointing. This is the same for spherical or cylindrical billboarding.
look.set(tmp).crs(axis); //Right cross axis gives you an adjusted
//look vector that is perpendicular to the axis, i.e. cylindrical billboarding.
decal.setRotation(look.nor(), axis); //Note that setRotation method requires
//direction vector to be normalized beforehand.
}
I didn't check to make sure the direction doesn't get flipped, because I draw it with back face culling turned off. So if you have culling on and don't see the sprite, that last cross product step might need to have its order reversed so the look vector points in the opposite direction.

Shadow map draws

After literally 3 days of finding out how to do shadowmaps without it going mental I finally reached a stage where it's acutally a visible shadow map. But now I have one last problem with some strange appearances of objects on places where it's impossible to be (At least in normal life).
The problem I have is this:
I draw 3 test objects in the scene a sphere, a weird blocky guy, and a smaller version of that blocky guy.
The sphere is basicly the closest to the light, so you won't expect the shadow of the big guy to appear on it.
The small guy is hovering in the air.
The big guy's shadow is on the correct position(He's also at position 0,0,0 in world coordinates), but has the sphere's shadow all over him on places which don't make sense.
This is the first image:
  
And the second one where I moved the big guy to a further location:
  
As you can see on the second image the shadow is also no longer on the sphere.
The order in which I draw them is Sphere -> Big guy -> Small guy
I also send the current ModelView matrix to the shader just before I draw each object
Sphere s = new Sphere();
glPushMatrix();
glTranslatef(100,150,-100);
s.draw(50, 36, 36);
glPopMatrix();
glPushMatrix();
glTranslatef(-50,0,50);
glScalef(0.1f,0.1f,0.1f);
glColor3f(1f,195f/255f,0f);
drawBot();
glPopMatrix();
glPushMatrix();
glTranslatef(0,0,100);
glRotatef(180,0,1,0);
glScalef(0.01f,0.01f,0.01f);
glColor3f(0,114f/255f,1f);
drawBot();
glPopMatrix();
This is my vertex shader:
#version 330 core
uniform mat4 lightP, lightMV, camP, camMV, bias, curMV;
out vec4 shadowCoord;
void main()
{
gl_Position = (camP*curMV) * gl_Vertex;
gl_Position.z-=0.01;
shadowCoord = (bias*(lightP*lightMV)) * (gl_Vertex);
}
This is my fragment shader:
#version 330 core
uniform sampler2D shadowMapTexture;
in vec4 shadowCoord;
out vec4 oColor;
float shadowMapping(){
float visibility = 0.0;
float bias = 0.01;
vec3 shadowPos = shadowCoord.xyz/shadowCoord.w;
if (texture2D( shadowMapTexture, shadowPos.xy ).z < shadowPos.z-bias){
visibility = 0.5;
}
return visibility;
}
void main(){
float shade = shadowMapping();
oColor = vec4(0,0,0,shade);
}
Is there anyone who understands this riddle?

GLSL renders textures wrong

I am trying to make a lighting system, the program changes the texture(block) brightness depends on the light it gets, and the program does it for every block(texture) that is visible to the player.
However, the lighting system works perfectly, but when it comes to rendering with shaders everything gets destroyed.
This code is in the render loop -
float light = Lighting.checkLight(mapPosY, mapPosX, this); // Returns the light the current block gets
map[mapPos].light = light; // Applies the light to the block
Shaders.block.setUniformf("lightBlock", light); // Sets the light value to the shader's uniform, to change the brightness of the current block / texture.
batch.draw(map[mapPos].TEXTURE, (mapPosX * Block.WIDTH), (mapPosY * Block.HEIGHT), Block.WIDTH, Block.HEIGHT); // Renders the block / texture to the screen.
The result is pretty random..
As i said the first two lines work perfectly, the problem is probably is in the third line or in the shader itself.
The shader:
Vertex shader -
attribute vec4 a_color;
attribute vec3 a_position;
attribute vec2 a_texCoord0;
uniform mat4 u_projTrans;
varying vec4 vColor;
varying vec2 vTexCoord;
void main() {
vColor = a_color;
vTexCoord = a_texCoord0;
gl_Position = u_projTrans * vec4(a_position, 1.0f);
}
Fragment shader -
varying vec4 vColor;
varying vec2 vTexCoord;
uniform vec2 screenSize;
uniform sampler2D tex;
uniform float lightBlock;
const float outerRadius = .65, innerRadius = .4, intensity = .6;
const float SOFTNESS = 0.6;
void main() {
vec4 texColor = texture2D(tex, vTexCoord) * vColor;
vec2 relativePosition = gl_FragCoord.xy / screenSize - .5;
float len = length(relativePosition);
float vignette = smoothstep(outerRadius, innerRadius, len);
texColor.rgb = mix(texColor.rgb, texColor.rgb * vignette * lightBlock, intensity);
gl_FragColor = texColor;
}
I fixed the problem.. but i dont have any idea why it fixed it.
Thanks to Pedro, i focused more on the render loop instead of the shader itself.
Before the loop i added those 2 lines -
List<Integer> lBlocks = new ArrayList<Integer>();
Shaders.block.setUniformf("lightBlock", 0.3f);
Basically I created an array to store the bright blocks later on.
I set the uniform of the shader to be 0.3f which means pretty dark. the value should be 0-1f.
Now in the render loop, inside the for -
float light = Lighting.checkLight(mapPosY, mapPosX, this);
map[mapPos].light = light;
if( light == 1.0f) {
lBlocks.add(mapPos);
} else {
batch.draw(map[mapPos].TEXTURE, (mapPosX * Block.WIDTH), (mapPosY * Block.HEIGHT), Block.WIDTH, Block.HEIGHT);
}
As you can see, the bright blocks i add to the array and the dark ones i render, i set the uniform to 0.3f before the render loop as you can in the first code sample.
After the render loop i loop again through the bright blocks.. because we didn't render them.
Shaders.block.setUniformf("lightBlock", 1.0f);
for(int i = 0; i < lBlocks.size(); i++) {
batch.draw(map[lBlocks.get(i)].TEXTURE, ((lBlocks.get(i) % width) * Block.WIDTH), ((lBlocks.get(i) / width) * Block.HEIGHT), Block.WIDTH, Block.HEIGHT);
}
Now i rendered the bright blocks and it works.. the result was good.
But I don't have any idea why its like that, its like cutting the render loop to two, one for dark blocks and one for the bright ones.
Thanks :)

Vertex world position in glsl, JOGL

so i've been trying to implement bump mapping for some time and i have it working in some way. So it renders the texture and shadowing correct but does not change as the light source moves around I determined that it applies the light source moving around from the source (0,0) and not where the light source is in the world. How do i determine the world position of the fragment in the shader? I am a bit stuck at the moment, any help would be appreciated.
--vertex shader
void main()
{
gl_TexCoord[0] = gl_MultiTexCoord0;
// Set the position of the current vertex
gl_Position = gl_ProjectionMatrix * gl_ModelViewMatrix * gl_Vertex;
}
--fragment shader
uniform sampler2D color_texture;
uniform sampler2D normal_texture;
uniform vec4 lightColor;
uniform vec3 falloff;
uniform vec3 lightPos;
uniform vec2 resolution;
uniform float ambience;
//uniform float lightDirection;
void main()
{
// Extract the normal from the normal map
vec3 normal = normalize(texture2D(normal_texture, gl_TexCoord[0].st).rgb * 2.0 - 1.0);
// Determine where the light is positioned
vec3 light_pos = normalize(lightPos);
//vec3 light_pos = normalize(vec3(1.0, 1.0, 0.5));
// Calculate the lighting diffuse value, the ambience is the darkness due to no light
float diffuse = max(dot(normal, light_pos), 0.0);
//direction
float lightDir = length(vec3(lightPos.xy - (gl_FragCoord.xy / resolution.xy), lightPos.z));
//calculate attenuation
float attenuation = 1.0 / ( falloff.x + (falloff.y*lightDir) + (falloff.z*lightDir*lightDir) );
//calculate the final color
vec3 color = diffuse * texture2D(color_texture, gl_TexCoord[0].st).rgb;
// Set the output color of our current pixel
gl_FragColor = vec4(color, 1.0);
}
--jogl, java code hooking up the shader
int shaderProgram = ShaderControl.enableShader(gl, shaderName);
//apply vars
int diffuseTextureVariableLocation = gl.getGL2().glGetUniformLocation(shaderProgram, "color_texture");
int normalColorVariableLocation = gl.getGL2().glGetUniformLocation(shaderProgram, "normal_texture");
int lightPositionVariableLocation = gl.getGL2().glGetUniformLocation(shaderProgram, "lightPos");
int lightColorVariableLocation = gl.getGL2().glGetUniformLocation(shaderProgram, "lightColor");
int falloffVariableLocation = gl.getGL2().glGetUniformLocation(shaderProgram, "falloff");
int resolutionVariableLocation = gl.getGL2().glGetUniformLocation(shaderProgram, "resolution");
int ambienceVariableLocation = gl.getGL2().glGetUniformLocation(shaderProgram, "ambience");
gl.getGL2().glUniform1i(diffuseTextureVariableLocation, 0);
gl.getGL2().glUniform1i(normalColorVariableLocation, 1);
gl.getGL2().glUniform3f(lightPositionVariableLocation, positionLight.x, positionLight.y, 1.5f);
gl.getGL2().glUniform4f(lightColorVariableLocation, 1f, 1.0f, 1.0f, 1f);
gl.getGL2().glUniform3f(falloffVariableLocation,.4f, 3f, 20f);
gl.getGL2().glUniform2f(resolutionVariableLocation, Game._viewPortDimension.width, Game._viewPortDimension.height);
gl.getGL2().glUniform1f(ambienceVariableLocation, 0.93f);
gl.getGL2().glActiveTexture(GL2.GL_TEXTURE1);
normalTexture.bind(gl);
//bind diffuse color to texture unit 0
gl.getGL2().glActiveTexture(GL2.GL_TEXTURE0);
texture.bind(gl);
//draw the texture and apply the bump mapping shader
drawTexture(gl, worldOffsetX, worldOffsetY, x, y, depth, rotation, percentageToDraw, width, height, texture);
ShaderControl.disableShader(gl);
Kind regards
Johandre
First, make sure you really need that. Once you are, you can create a varying vec3 in your fragment shaders that gets interpolated from the vertex shader that holds the world position. In order to do that, make sure you have separate modelview matrix and projection matrix. (I prefer having only a projection matrix for the games I made so far). Use the output of the modelview matrix for your varying vec3.

Categories