Incorrect lighting of a bone-animated object on OpenGL ES 2.0 - opengl-es

I imported a simple animated object from Blender on three bones. I have a problem with lighting of skinning object. I set a light position above the object:
const vec3 lightPosition = vec3(0.0, 15.0, 0.0);
You can see that lighting is affected under object too:
precision mediump float;
attribute vec3 aPosition;
attribute vec4 aNormal;
attribute vec2 aTexCoord;
attribute vec3 aJoints;
attribute vec3 aWeights;
uniform mat4 uMvpMatrix;
uniform mat4 uModelMatrix;
uniform mat4 uNormalMatrix;
uniform mat4 uTransforms[3];
varying vec3 vPosition;
varying vec3 vNormal;
varying vec2 vTexCoord;
void main()
{
vec4 totalLocalPos = vec4(0.0);
vec4 totalNormal = vec4(0.0);
for (int i = 0; i < 3; i++)
{
int jointIndex = int(aJoints[i]);
mat4 jointTransform = uTransforms[jointIndex];
vec4 posePosition = jointTransform * vec4(aPosition, 1.0);
totalLocalPos += posePosition * aWeights[i];
vec4 worldNormal = jointTransform * aNormal;
totalNormal += worldNormal * aWeights[i];
}
gl_Position = uMvpMatrix * totalLocalPos;
vPosition = vec3(uModelMatrix * vec4(aPosition, 1.0));
vNormal = totalNormal.xyz;
vTexCoord = aTexCoord;
}
precision mediump float;
const vec3 lightColor = vec3(0.8, 0.8, 0.8);
const vec3 lightPosition = vec3(0.0, 15.0, 0.0);
const vec3 ambientLight = vec3(0.3, 0.3, 0.3);
uniform sampler2D uSampler;
varying vec3 vPosition;
varying vec3 vNormal;
varying vec2 vTexCoord;
void main()
{
vec4 color = texture2D(uSampler, vTexCoord);
vec3 normal = normalize(vNormal);
vec3 lightDirection = normalize(lightPosition - vPosition);
float nDotL = max(dot(lightDirection, normal), 0.0);
vec3 diffuse = lightColor * color.rgb * nDotL;
vec3 ambient = ambientLight * color.rgb;
gl_FragColor = vec4(diffuse + ambient, color.a);
}

Aroch helped me on Russian forum here:
the normal only needs to be rotated.
Now it works as it should. I took shaders (except lighting model) from source for a video tutorial series from ThinMatrix. It works fine for ThinMatrix. This means that for some reason its lighting model is not affected by the translation of the normal vector. It will be necessary to figure out later why he does not have this problem. I made the transfer of the array of rotation matrices a separate uniform:
uniform mat4 uTransforms[3];
uniform mat4 uRotations[3];
...
for (int i = 0; i < 3; i++)
{
int jointIndex = int(aJoints[i]);
mat4 jointTransform = uTransforms[jointIndex];
vec4 posePosition = jointTransform * vec4(aPosition, 1.0);
totalLocalPos += posePosition * aWeights[i];
mat4 rotation = uRotations[jointIndex];
vec4 worldNormal = rotation * aNormal;
totalNormal += worldNormal * aWeights[i];
}
Gif Animation: https://gamedev.ru/files/images/solution-with-lighting.gif

Related

Per instance UV texture mapping in Three.js InstancedBufferGeometry

I have a InstancedBufferGeometry made up of a single Plane:
const plane = new THREE.PlaneBufferGeometry(100, 100, 1, 1);
const geometry = new THREE.InstancedBufferGeometry();
geometry.maxInstancedCount = 100;
geometry.attributes.position = plane.attributes.position;
geometry.index = plane.index;
geometry.attributes.uv = plane.attributes.uv;
geometry.addAttribute( 'offset', new THREE.InstancedBufferAttribute( new Float32Array( offsets ), 3 ) ); // an offset position
I am applying a texture to each plane, which is working as expected, however I wish to apply a different region of the texture to each instance, and I'm not sure about the correct approach.
At the moment I have tried to build up uv's per instance, based on the structure of the uv's for a single plane:
let uvs = [];
for (let i = 0; i < 100; i++) {
const tl = [0, 1];
const tr = [1, 1];
const bl = [0, 0];
const br = [1, 0];
uvs = uvs.concat(tl, tr, bl, br);
}
...
geometry.addAttribute( 'uv', new THREE.InstancedBufferAttribute( new Float32Array( uvs ), 2) );
When I do this, I don't have any errors, but every instance is just a single colour (all instances are the the same colour). I have tried changing the instance size, and also the meshes per attribute (which I don't fully understand, struggling to find a good explanation in the docs).
I feel like I'm close, but I'm missing something, so a point in the right direction would be fantastic!
(For reference, here are my shaders):
const vertexShader = `
precision mediump float;
uniform vec3 color;
uniform sampler2D tPositions;
uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;
attribute vec2 uv;
attribute vec2 dataUv;
attribute vec3 position;
attribute vec3 offset;
attribute vec3 particlePosition;
attribute vec4 orientationStart;
attribute vec4 orientationEnd;
varying vec3 vPosition;
varying vec3 vColor;
varying vec2 vUv;
void main(){
vPosition = position;
vec4 orientation = normalize( orientationStart );
vec3 vcV = cross( orientation.xyz, vPosition );
vPosition = vcV * ( 2.0 * orientation.w ) + ( cross( orientation.xyz, vcV ) * 2.0 + vPosition );
vec4 data = texture2D( tPositions, vec2(dataUv.x, 0.0));
vec3 particlePosition = (data.xyz - 0.5) * 1000.0;
vUv = uv;
vColor = data.xyz;
gl_Position = projectionMatrix * modelViewMatrix * vec4( position + particlePosition + offset, 1.0 );
}
`;
const fragmentShader = `
precision mediump float;
uniform sampler2D map;
varying vec3 vPosition;
varying vec3 vColor;
varying vec2 vUv;
void main() {
vec3 color = texture2D(map, vUv).xyz;
gl_FragColor = vec4(color, 1.0);
}
`;
As all my instances need to take the same size rectangular area, but offset (like a sprite sheet), I have added a UV offset and UV scale attribute to each instance, and use this to define which area of the map to use:
const uvOffsets = [];
for (let i = 0; i < INSTANCES; i++) {
const u = i % textureWidthHeight;
const v = ~~ (i / textureWidthHeight);
uvOffsets.push(u, v);
}
...
geometry.attributes.uv = plane.attributes.uv;
geometry.addAttribute( 'uvOffsets', new THREE.InstancedBufferAttribute( new Float32Array( uvOffsets ), 2 ) );
uniforms: {
...
uUvScale: { value: 1 / textureWidthHeight }
}
And in the fragment shader:
void main() {
vec4 color = texture2D(map, (vUv * uUvScale) + (vUvOffsets * uUvScale));
gl_FragColor = vec4(1.0, 1.0, 1.0, color.a);
}
\o/

Three.JS: Gaussian blur in GLSL shader

I have this vert/frag shader, which is using vertex data and two textures.
I am trying to apply post blur effect, but having only rectangles after it.
vert:
attribute float type;
attribute float size;
attribute float phase;
attribute float increment;
uniform float time;
uniform vec2 resolution;
uniform sampler2D textureA;
uniform sampler2D textureB;
varying float t;
void main() {
t = type;
vec4 mvPosition = modelViewMatrix * vec4(position, 1.0 );
if(t == 0.) {
gl_PointSize = size * 0.8;
} else {
gl_PointSize = size * sin(phase + time * increment) * 12.;
}
gl_Position = projectionMatrix * mvPosition;
}
frag:
uniform float time;
uniform vec2 resolution;
uniform sampler2D textureA;
uniform sampler2D textureB;
varying float t;
uniform sampler2D texture;
vec4 blur2D(sampler2D image, vec2 uv, vec2 resolution, vec2 direction) {
vec4 color = vec4(0.0);
vec2 off1 = vec2(1.3846153846) * direction;
vec2 off2 = vec2(3.2307692308) * direction;
color += texture2D(image, uv) * 0.2270270270;
color += texture2D(image, uv + (off1 / resolution)) * 0.3162162162;
color += texture2D(image, uv - (off1 / resolution)) * 0.3162162162;
color += texture2D(image, uv + (off2 / resolution)) * 0.0702702703;
color += texture2D(image, uv - (off2 / resolution)) * 0.0702702703;
return color;
}
void main() {
vec2 direction = vec2(1., 0.);
vec2 uv = vec2(gl_FragCoord.xy / resolution.xy);
gl_FragColor = vec4(vec3(1.0, 1.0, 1.0), 1.);
if(t == 0.){
gl_FragColor = gl_FragColor * texture2D(textureA, gl_PointCoord);
} else {
gl_FragColor = gl_FragColor * texture2D(textureB, gl_PointCoord);
}
gl_FragColor = blur2D(texture, uv, resolution.xy, direction);
}
How could I 'bake' everything before applying blurring to texture2D/sampler2D?
Maybe I need to create another blur shader and pass texture2D to it?

Shadow artifacts in opengl

I am trying to render an object and two lights, one of the lights cast shadows. Everything works ok but I noticed that there are some obvious artifacts, as shown in the below image, some shadows seem to overflow to bright areas.
Below is the shaders to render depth information into a framebuffer
<script id="shadow-shader-vertex" type="x-shader/x-vertex">
attribute vec4 aVertexPosition;
uniform mat4 uObjMVP;
void main() {
gl_Position = uObjMVP * aVertexPosition;
}
</script>
<script id="shadow-shader-fragment" type="x-shader/x-vertex">
precision mediump float;
void main() {
//pack gl_FragCoord.z
const vec4 bitShift = vec4(1.0, 256.0, 256.0 * 256.0, 256.0 * 256.0 * 256.0);
const vec4 bitMask = vec4(1.0/256.0, 1.0/256.0, 1.0/256.0, 0.0);
vec4 rgbaDepth = fract(gl_FragCoord.z * bitShift);
rgbaDepth -= rgbaDepth.gbaa * bitMask;
gl_FragColor = rgbaDepth;
}
</script>
In the above shaders, uObjMVP is the MVP matrix used when looking from the position of the light that cast shadow (the warm light, the cold light does not cast shadow)
And here are the shaders to draw everything:
<script id="shader-vertex" type="x-shader/x-vertex">
//position of a vertex.
attribute vec4 aVertexPosition;
//vertex normal.
attribute vec3 aNormal;
//mvp matrix
uniform mat4 uObjMVP;
uniform mat3 uNormalMV;
//shadow mvp matrix
uniform mat4 uShadowMVP;
//interplate normals
varying vec3 vNormal;
//for shadow calculation
varying vec4 vShadowPositionFromLight;
void main() {
gl_Position = uObjMVP * aVertexPosition;
//convert normal direction from object space to view space
vNormal = uNormalMV * aNormal;
vShadowPositionFromLight = uShadowMVP * aVertexPosition;
}
</script>
<script id="shader-fragment" type="x-shader/x-fragment">
precision mediump float;
uniform sampler2D uShadowMap;
varying vec3 vNormal;
varying vec4 vShadowPositionFromLight;
struct baseColor {
vec3 ambient;
vec3 diffuse;
};
struct directLight {
vec3 direction;
vec3 color;
};
baseColor mysObjBaseColor = baseColor(
vec3(1.0, 1.0, 1.0),
vec3(1.0, 1.0, 1.0)
);
directLight warmLight = directLight(
normalize(vec3(-83.064, -1.99, -173.467)),
vec3(0.831, 0.976, 0.243)
);
directLight coldLight = directLight(
normalize(vec3(37.889, 47.864, -207.187)),
vec3(0.196, 0.361, 0.608)
);
vec3 ambientLightColor = vec3(0.3, 0.3, 0.3);
float unpackDepth(const in vec4 rgbaDepth) {
const vec4 bitShift = vec4(1.0, 1.0/256.0, 1.0/(256.0*256.0), 1.0/(256.0*256.0*256.0));
float depth = dot(rgbaDepth, bitShift);
return depth;
}
float calVisibility() {
vec3 shadowCoord = (vShadowPositionFromLight.xyz/vShadowPositionFromLight.w)/2.0 + 0.5;
float depth = unpackDepth(texture2D(uShadowMap, shadowCoord.xy));
return (shadowCoord.z > depth + 0.005) ? 0.4 : 1.0;
}
vec3 calAmbientLight(){
return ambientLightColor * mysObjBaseColor.ambient;
}
vec3 calDiffuseLight(const in directLight light, const in float visibility){
vec3 inverseLightDir = light.direction * -1.0;
float dot = max(dot(inverseLightDir, normalize(vNormal)), 0.0);
return light.color * mysObjBaseColor.diffuse * dot * visibility;
}
void main() {
vec3 ambientLight = calAmbientLight();
float visibility = calVisibility();
vec3 warmDiffuseLight = calDiffuseLight(warmLight, visibility);
// cold light does not cast shadow and hence visilibility is always 1.0
vec3 coldDiffuseLight = calDiffuseLight(coldLight, 1.0);
gl_FragColor = vec4(coldDiffuseLight + warmDiffuseLight + ambientLight, 1.0);
}
</script>
If I simply draw the depth information out on to the canvas,
void main() {
// vec3 ambientLight = calAmbientLight();
// float visibility = calVisibility();
// vec3 warmDiffuseLight = calDiffuseLight(warmLight, visibility);
// // cold light does not cast shadow and hence visilibility is always 1.0
// vec3 coldDiffuseLight = calDiffuseLight(coldLight, 1.0);
// gl_FragColor = vec4(coldDiffuseLight + warmDiffuseLight + ambientLight, 1.0);
vec3 shadowCoord = (vShadowPositionFromLight.xyz/vShadowPositionFromLight.w)/2.0 + 0.5;
gl_FragColor = vec4(unpackDepth(texture2D(uShadowMap, shadowCoord.xy)), 0.0, 0.0, 1.0);
}
I would get this image
Thanks in advance.

opengles 2.0 per-vertex lighting

VertexShader:
varying vec4 diffuseColor;
uniform mat4 modelViewProj;
uniform mat4 modelViewTranspose;
attribute vec3 streamNormal;
attribute vec3 streamPosition;
void main() {
gl_Position = modelViewProj * streamPosition;
diffuseColor = max(0, dot(normalize(modelViewTranspose * streamNormal), vec3(0,0,1)) * vec4(1,0,0,1);
}
PixelShader:
varying vec4 diffuseColor;
void main() {
gl_FragColor = diffuseColor;
}
the matrix is by left-hand (row major), modelViewProj calculated by C++, it is the world * view * projection, modelViewTranspose = transpose(inverse(world * view)).
The render result is not right, I cannot see any thing, but if I set gl_FragColor always to vec4(1,1,1,1), the model render is no problem. Does my code have errors? or other problems?

WebGL lighting isn't applied to some edges?

No idea what this is called, but when viewing my model at a distance, it doesn't look like the lighting is being applied to some of the edges, which makes them stand out. Any ideas? I'm not using any textures.
Vertex Shader:
attribute vec3 aPosition;
attribute vec3 aNormal;
uniform mat4 uMVMatrix;
uniform mat4 uPMatrix;
uniform mat4 uNormalMatrix;
varying vec3 vLighting;
void main(void) {
vec3 ambientLight = vec3(0.2, 0.2, 0.2);
vec3 directionalLight = vec3(1.0, 0.5, 0.5);
vec3 directionalLightDirection = vec3(0.8, 0.7, 0.6);
vec4 transformedNormal = uNormalMatrix * vec4(aNormal, 1.0);
float directional = max(dot(transformedNormal.xyz, directionalLightDirection), 0.0);
vLighting = ambientLight + (directionalLight * directional);
gl_Position = uPMatrix * uMVMatrix * vec4(aPosition / 80.0, 1.0);
}
Fragment Shader:
precision mediump float;
varying vec3 vLighting;
void main(void) {
vec4 skin = vec4(0.8, 0.6, 0.4, 1.0);
gl_FragColor = vec4(skin.rgb * vLighting, skin.a);
}
Picture:

Resources