Shader linking fails without error message only on mobile devices - javascript

I'm working on a graphics engine written in C++. We currently can either build for desktop or for web (using emscripten). Both platforms utilize OpenGL ES 2.0.
I have a vertex and a fragment shader:
Vertex Shader
#version 100
attribute vec4 a_position;
attribute vec2 a_texcoord;
attribute vec3 a_normal;
attribute vec3 a_tangent;
attribute vec3 a_bitangent;
uniform mat4 u_model;
uniform mat4 u_view;
uniform mat4 u_projection;
uniform mat3 u_normalMatrix;
varying vec2 v_texcoord;
varying vec3 v_normal;
varying vec3 v_fragPos;
varying mat3 v_TBN;
void main()
{
vec3 T = normalize(vec3(u_model * vec4(a_tangent, 0.0)));
vec3 B = normalize(vec3(u_model * vec4(a_bitangent, 0.0)));
vec3 N = normalize(vec3(u_model * vec4(a_normal, 0.0)));
v_TBN = mat3(T, B, N);
gl_Position = u_projection * u_view * u_model * a_position;
v_texcoord = a_texcoord;
v_normal = u_normalMatrix * a_normal;
v_fragPos = vec3(u_model * a_position);
};
Fragment Shader
#version 100
precision mediump float;
varying vec2 v_texcoord;
varying vec3 v_normal;
varying vec3 v_fragPos;
varying mat3 v_TBN;
struct Material {
sampler2D diffuse;
sampler2D specular;
sampler2D normal;
float shininess;
};
struct Light {
int type;
vec3 position;
vec3 direction;
vec3 ambient;
vec3 diffuse;
vec3 specular;
float constant;
float linear;
float quadratic;
float cutOff;
float outerCutOff;
};
const int MAX_LIGHTS = 16;
uniform vec3 u_viewPos;
uniform Material u_material;
uniform Light u_lights[MAX_LIGHTS];
uniform int u_lightCount;
uniform bool normalMapping;
vec3 calcDirLight(Light light, vec3 normal, vec3 viewDir);
vec3 calcPointLight(Light light, vec3 normal, vec3 viewDir);
vec3 calcSpotLight(Light light, vec3 normal, vec3 viewDir);
void main()
{
vec3 lightOutput = vec3(0.0);
vec3 norm = normalize(v_normal);
if (normalMapping)
{
norm = texture2D(u_material.normal, v_texcoord).rgb;
norm = normalize(norm * 2.0 - 1.0);
norm = normalize(v_TBN * norm);
}
vec3 viewDir = normalize(u_viewPos - v_fragPos);
for(int i = 0; i < MAX_LIGHTS; i++)
{
if (i >= u_lightCount){break;}
if (u_lights[i].type == 0)
{
lightOutput += calcDirLight(u_lights[i], norm, viewDir)
}
else if (u_lights[i].type == 1)
{
lightOutput += calcPointLight(u_lights[i], norm, viewDir);
}
else if (u_lights[i].type == 2)
{
lightOutput += calcSpotLight(u_lights[i], norm, viewDir);
}
}
gl_FragColor = vec4(lightOutput, 1.0);
}
vec3 calcDirLight(Light light, vec3 normal, vec3 viewDir)
{
vec3 lightDir = normalize(-light.direction);
float diff = max(dot(normal, lightDir), 0.0);
vec3 reflectDir = reflect(-lightDir, normal);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), u_material.shininess);
vec3 ambient = light.ambient * vec3(texture2D(u_material.diffuse, v_texcoord));
vec3 diffuse = light.diffuse * diff * vec3(texture2D(u_material.diffuse, v_texcoord));
vec3 specular = light.specular * spec * vec3(texture2D(u_material.specular, v_texcoord));
return (ambient + diffuse + specular);
}
vec3 calcPointLight(Light light, vec3 normal, vec3 viewDir)
{
vec3 lightDir = normalize(light.position - v_fragPos);
float diff = max(dot(normal, lightDir), 0.0);
vec3 reflectDir = reflect(-lightDir, normal);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), u_material.shininess);
float distance = length(light.position - v_fragPos);
float attenuation = 1.0 / (light.constant + light.linear * distance + light.quadratic * (distance * distance));
vec3 ambient = light.ambient * vec3(texture2D(u_material.diffuse, v_texcoord));
vec3 diffuse = light.diffuse * diff * vec3(texture2D(u_material.diffuse, v_texcoord));
vec3 specular = light.specular * spec * vec3(texture2D(u_material.specular, v_texcoord));
ambient *= attenuation;
diffuse *= attenuation;
specular *= attenuation;
return (ambient + diffuse + specular);
}
vec3 calcSpotLight(Light light, vec3 normal, vec3 viewDir)
{
vec3 lightDir = normalize(light.position - v_fragPos);
float diff = max(dot(normal, lightDir), 0.0);
vec3 reflectDir = reflect(-lightDir, normal);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), u_material.shininess);
float distance = length(light.position - v_fragPos);
float attenuation = 1.0 / (light.constant + light.linear * distance + light.quadratic * (distance * distance));
float theta = dot(lightDir, normalize(light.direction));
float epsilon = light.cutOff - light.outerCutOff;
float intensity = clamp((theta - light.outerCutOff) / epsilon, 0.0, 1.0);
vec3 ambient = light.ambient * vec3(texture2D(u_material.diffuse, v_texcoord));
vec3 diffuse = light.diffuse * diff * vec3(texture2D(u_material.diffuse, v_texcoord));
vec3 specular = light.specular * spec * vec3(texture2D(u_material.specular, v_texcoord));
ambient *= attenuation * intensity;
diffuse *= attenuation * intensity;
specular *= attenuation * intensity;
return (ambient + diffuse + specular);
};
They both compile without errors, but the linking stage fails. The info log (accessed using glGetProgramInfoLog) is empty.
The biggest problem is that it compiles and links perfectly fine on desktop native and desktop browsers (emscripten). It only fails linking on mobile devices.
I've spent hours trying to figure this out, without success. Is there anything obvious i might have missed?
Edit 1: This is the code that builds the shader program:
GLuint GLES2Shader::buildProgram(GLuint vertexShader, GLuint fragmentShader)
{
GL( GLuint programObject = glCreateProgram() );
GL( glAttachShader(programObject, vertexShader) );
GL( glAttachShader(programObject, fragmentShader) );
GL( glLinkProgram(programObject) );
//check if the program linked successfully
GLint linked;
GL( glGetProgramiv(programObject, GL_LINK_STATUS, &linked) );
if (!linked)
{
DEBUG( GLchar infoLog[512] );
DEBUG( glGetProgramInfoLog(programObject, 512, NULL, infoLog) );
DEBUG( LOGE("ERROR::SHADER::LINKING_FAILED %s", infoLog) );
GL( glDeleteProgram(programObject) );
return 0;
}
return programObject;
}

Related

Adding fog offset to Shader material

Hi so I am trying to add an offset to the fog on my shader texture
I've managed to do this before but now i have changed the code slightly to use uv repeating and im not sure how to apply viewDirection to vec3 txl here's the code:
varying vec2 vuv;
uniform vec2 repeat;
uniform vec3 topColor;
uniform vec3 bottomColor;
uniform float offset;
uniform float exponent;
uniform vec3 fogColor;
uniform float fogNear;
uniform float fogFar;
uniform float fogDensity;
uniform sampler2D background;
varying vec3 vWorldPosition;
void main() {
vec3 viewDirection = normalize(vWorldPosition - cameraPosition);
// orginal this worked with fog
// vec3 stars = textureCube(background, viewDirection).xyz;
vec2 uv = vuv;
uv = fract(uv * repeat);
vec2 smooth_uv = repeat * vuv;
vec4 duv = vec4(dFdx(smooth_uv), dFdy(smooth_uv));
vec3 txl = textureGrad(background, uv, duv.xy, duv.zw).rgb;
float h = normalize(vWorldPosition + offset).y;
float t = max(pow(max(h, 0.0), exponent), 0.0);
float f = exp(min(0.0, -vWorldPosition.y * 0.00125));
vec3 sky = mix(txl, bottomColor, f);
gl_FragColor = vec4(sky, 1.0);
}`;
In my code code i did
vec3 stars = textureCube(background, viewDirection).xyz;
float h = normalize(vWorldPosition + offset).y;
float f = exp(min(0.0, -vWorldPosition.y * 0.00125));
this worked fine but because
I have changed the first line to this vec3 txl = textureGrad(background, uv, duv.xy, duv.zw).rgb; I no longer have viewDirection, which is the vWorldPosition that the offset goes off so my question really is this...
how can i add viewDirection to vec3 txl = textureGrad(background, uv, duv.xy, duv.zw).rgb; so the offset works again?
thanks!

Is it possible to have the objects I draw not have the image I set as the background?

I have this program where my canvas assumes a WebGL context and I'm trying to upload an image as the background, but when I do so, everything becomes the image itself:
I achieved this by fiddling with my css files and setting the background-image: to what I wanted. But I would really like if only the background itself would be the image, and not the objects I draw.
Any tips?
This is my shader.frag code:
precision highp float;
const int MAX_LIGHTS = 8;
struct LightInfo {
bool active;
bool directional;
vec3 pos;
vec3 ia;
vec3 id;
vec3 is;
};
struct MaterialInfo {
vec3 ka;
vec3 kd;
vec3 ks;
float shininess;
};
varying vec3 fNormal;
varying vec3 posC;
uniform int uNLights; // Effective number of lights used
uniform LightInfo uLight[MAX_LIGHTS]; // The array of lights present in the scene
uniform MaterialInfo uMaterial; // The material of the object being drawn
uniform mat4 mView;
uniform mat4 mViewNormals;
void main() {
for(int i = 0; i < MAX_LIGHTS; i++) {
if(i == uNLights) break;
if(uLight[i].active) {
vec3 L;
if(uLight[i].directional) {
L = normalize((mViewNormals * vec4(uLight[i].pos, 0.0)).xyz);
} else {
L = normalize((mView * vec4(uLight[i].pos, 1.0)).xyz - posC);
}
vec3 V = normalize(-posC);
vec3 N = normalize(fNormal);
vec3 R = reflect(-L, N);
float diffuseFactor = max(dot(N, L), 0.0);
float specularFactor = pow(max(dot(R, V), 0.0), uMaterial.shininess);
vec3 ambientColor = uLight[i].ia * uMaterial.ka;
vec3 diffuseColor = uLight[i].id * uMaterial.kd;
vec3 diffuse = diffuseFactor * diffuseColor;
vec3 specularColor = uLight[i].is * uMaterial.ks;
vec3 specular = specularFactor * specularColor;
if(dot(L, N) < 0.0) {
specular = vec3(0.0, 0.0, 0.0);
}
gl_FragColor.xyz += vec3(ambientColor + diffuse + specular);
}
}
}

Normal Mapping on Texture Splatted Terrain

I have developed a terrain generation system with texture splatting in Three.js and I am having trouble applying normal mapping to it. How should I go about it? This code is fully functional as of now.
I have looked at some tutorials of WebGL shading and normal mapping and I could not find one to accurately fit my code.
FRAGMENT_SHADER: `
uniform sampler2D albedoA;
uniform sampler2D albedoB;
uniform sampler2D albedoC;
uniform sampler2D albedoD;
uniform sampler2D albedoE;
uniform sampler2D normalA;
uniform sampler2D normalB;
uniform sampler2D normalC;
uniform sampler2D normalD;
uniform sampler2D normalE;
uniform float repeatScale;
uniform vec3 sunPosition;
varying vec2 vUV;
varying float vAmount;
varying vec3 vNormal;
varying vec3 vWorldPosition;
void main()
{
vec3 diffA = (smoothstep(0.01, 0.25, vAmount) - smoothstep(0.25, 0.35, vAmount)) * texture2D(albedoA, vUV * repeatScale).rgb;
vec3 diffB = (smoothstep(0.24, 0.27, vAmount) - smoothstep(0.27, 0.37, vAmount)) * texture2D(albedoB, vUV * repeatScale).rgb;
vec3 diffC = (smoothstep(0.28, 0.32, vAmount) - smoothstep(0.32, 0.42, vAmount)) * texture2D(albedoC, vUV * repeatScale).rgb;
vec3 diffD = (smoothstep(0.30, 0.60, vAmount) - smoothstep(0.40, 0.70, vAmount)) * texture2D(albedoD, vUV * repeatScale).rgb;
vec3 diffE = (smoothstep(0.50, 0.85, vAmount)) * texture2D(albedoE, vUV * repeatScale).rgb;
vec3 albedoVector = diffA + diffB + diffC + diffD + diffE;
vec3 normA = (smoothstep(0.01, 0.25, vAmount) - smoothstep(0.25, 0.35, vAmount)) * texture2D(normalA, vUV * repeatScale).rgb;
vec3 normB = (smoothstep(0.24, 0.27, vAmount) - smoothstep(0.27, 0.37, vAmount)) * texture2D(normalB, vUV * repeatScale).rgb;
vec3 normC = (smoothstep(0.28, 0.32, vAmount) - smoothstep(0.32, 0.42, vAmount)) * texture2D(normalC, vUV * repeatScale).rgb;
vec3 normD = (smoothstep(0.30, 0.60, vAmount) - smoothstep(0.40, 0.70, vAmount)) * texture2D(normalD, vUV * repeatScale).rgb;
vec3 normE = (smoothstep(0.50, 0.85, vAmount)) * texture2D(normalE, vUV * repeatScale).rgb;
vec3 normalVector = normA + normB + normC + normD + normE;
float diffuseFloat = max(dot(normalize(sunPosition - vWorldPosition), vNormal), 0.0);
if (diffuseFloat < 0.25) { diffuseFloat = 0.25; }
if (diffuseFloat > 1.0) { diffuseFloat = 1.0; }
gl_FragColor = vec4(diffuseFloat * albedoVector, 1.0);
}
`,
VERTEX_SHADER: `
uniform sampler2D heightTexture;
varying vec2 vUV;
varying float vAmount;
varying vec3 vNormal;
varying vec3 vWorldPosition;
void main()
{
vUV = uv;
vAmount = texture2D(heightTexture, uv).r;
vec4 worldPosition = modelViewMatrix * vec4(position, 1.0);
vWorldPosition = worldPosition.xyz;
vNormal = vec3(normal);
gl_Position = projectionMatrix * worldPosition;
}
`
I am using the THREE.ShaderMaterial and as of now the code splats the textures and accounts for lighting and shadows, but not the normal maps.
You can calculate the normal of a surface (terrain) by the simple numerical approximation - See Finite difference method.
You have to know the size of the height map texture. I recommend to set the size of the texture to a uniform variable:
uniform sampler2D heightTexture;
uniform vec2 heightTextureSize; // (width, height) of heightTexture;
Calculate the offset between adjacent texels of the texture:
vec2 offset = 1.0 / heightTextureSize;
Read the height of the adjacent texels of the texture
vA = texture2D(heightTexture, uv).r;
vAL = texture2D(heightTexture, uv + vec2(-offset.x, 0.0)).r;
vAR = texture2D(heightTexture, uv + vec2( offset.x, 0.0)).r;
vAB = texture2D(heightTexture, uv + vec2( 0.0, -offset.y)).r;
vAT = texture2D(heightTexture, uv + vec2( 0.0, offset.y)).r;
Finally you can calculate the approximated normal vector in texture space
vec3 normalMap = normalize( vec3(vAL - vAR, vAT - vAB, 2.0) );

Three.js, GLSL, HeightMaps + Alphamaps

I am using three.js to attempt and learn more about OpenGL. Anyway, I've created a fragment shader which actually works for blending textures with an alphamap but I am having issues with the heightmap portion. If anyone has any ideas please let me know as I am stuck. Oh, I have used this as a reference: http://threejs.org/examples/webgl_terrain_dynamic.html.
<script id="vertexShader" type="x-shader/x-vertex">
uniform sampler2D tDisplacement;
varying vec2 vUv;
void main(void)
{
vUv = uv;
#ifdef VERTEX_TEXTURES
vec3 dv = texture2D( tDisplacement, uv ).xyz;
float df = 300.0 * dv.z + 1.0;
vec3 displacedPosition = normal * df + position;
vec4 worldPosition = modelMatrix * vec4( displacedPosition, 1.0 );
vec4 mvPosition = modelViewMatrix * vec4( displacedPosition, 1.0 );
#else
vec4 worldPosition = modelMatrix * vec4( position, 1.0 );
vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
#endif
gl_Position = projectionMatrix * mvPosition;
}
</script>
<script id="fragmentShaderNoise" type="x-shader/x-fragment">
uniform sampler2D Alpha;
uniform sampler2D Grass;
uniform sampler2D Stone;
uniform sampler2D Rock;
uniform sampler2D tDisplacement;
varying vec3 vertex_color;
varying vec2 vUv;
void main(void)
{
vec4 alpha = texture2D( Alpha, vUv.xy);
vec4 tex0 = texture2D( Grass, vUv.xy * 10.0 ); // Tile
vec4 tex1 = texture2D( Rock, vUv.xy * 10.0 ); // Tile
vec4 tex2 = texture2D( Stone, vUv.xy * 10.0 ); // Tile
tex0 *= alpha.r; // Red channel
tex1 = mix( tex0, tex1, alpha.g ); // Green channel
vec4 outColor = mix( tex1, tex2, alpha.b ); // Blue channel
gl_FragColor = outColor;
}
</script>
If a related example would help, I have written a demo that includes a vertex shader that incorporates displacement at:
http://stemkoski.github.io/Three.js/Shader-Fireball.html
Alternatively, perhaps you could start with something simpler, and then work incrementally towards your desired result. For example, I believe that the code below would allow you to change the vertex position according to the red amount of each pixel in the texture bumpTexture.
uniform sampler2D bumpTexture;
varying vec2 vUv;
void main()
{
vUv = uv;
vec4 bumpData = texture2D( bumpTexture, uv );
float displacement = 100.0 * bumpData.r;
vec3 newPosition = position + normal * displacement;
gl_Position = projectionMatrix * modelViewMatrix * vec4( newPosition, 1.0 );
}
Hope this helps!

Debug GLSL code in webgl

Is it possible to debug GLSL code or print the variable values from within the glsl code while using it with webgl ? Do three.js or scene.js contain any such functionality?
Not really,
The way I usually debug GLSL is to output colors. So for example, given 2 shaders like
// vertex shader
uniform mat4 worldViewProjection;
uniform vec3 lightWorldPos;
uniform mat4 world;
uniform mat4 viewInverse;
uniform mat4 worldInverseTranspose;
attribute vec4 position;
attribute vec3 normal;
attribute vec2 texCoord;
varying vec4 v_position;
varying vec2 v_texCoord;
varying vec3 v_normal;
varying vec3 v_surfaceToLight;
varying vec3 v_surfaceToView;
void main() {
v_texCoord = texCoord;
v_position = (worldViewProjection * position);
v_normal = (worldInverseTranspose * vec4(normal, 0)).xyz;
v_surfaceToLight = lightWorldPos - (world * position).xyz;
v_surfaceToView = (viewInverse[3] - (world * position)).xyz;
gl_Position = v_position;
}
// fragment-shader
precision highp float;
uniform vec4 colorMult;
varying vec4 v_position;
varying vec2 v_texCoord;
varying vec3 v_normal;
varying vec3 v_surfaceToLight;
varying vec3 v_surfaceToView;
uniform sampler2D diffuseSampler;
uniform vec4 specular;
uniform sampler2D bumpSampler;
uniform float shininess;
uniform float specularFactor;
vec4 lit(float l ,float h, float m) {
return vec4(1.0,
max(l, 0.0),
(l > 0.0) ? pow(max(0.0, h), m) : 0.0,
1.0);
}
void main() {
vec4 diffuse = texture2D(diffuseSampler, v_texCoord) * colorMult;
vec3 normal = normalize(v_normal);
vec3 surfaceToLight = normalize(v_surfaceToLight);
vec3 surfaceToView = normalize(v_surfaceToView);
vec3 halfVector = normalize(surfaceToLight + surfaceToView);
vec4 litR = lit(dot(normal, surfaceToLight),
dot(normal, halfVector), shininess);
gl_FragColor = vec4((
vec4(1,1,1,1) * (diffuse * litR.y
+ specular * litR.z * specularFactor)).rgb,
diffuse.a);
}
If I didn't see something on the screen I'd first change the fragment shader to by just adding a line at the end
gl_FragColor = vec4(1,0,0,1); // draw red
If I started to see my geometry then I'd know the issue is probably in the fragment shader. I might check my normals by doing this
gl_FragColor = vec4(v_normal * 0.5 + 0.5, 1);
If the normals looked okay I might check the UV coords with
gl_FragColor = vec4(v_texCoord, 0, 1);
etc...
You can try WebGL-Inspector for this purpose.

Categories