Using the same uniform variable between two shaders in WebGL - javascript

I am using WebGL to try and render a scene with both color and textures, with the colors being rendered through the vertex shader and the textures being rendered through the fragment shader. Both shaders utilize some of the same uniform variables, however WebGL does not allow me to pass them between both, only one or the other. The error message I receive is "Uniform variable not linkable between attached shaders." I have gone through similar posts by other users regarding this issue however I was not able to find one that matched my situation. My code is as follows:
const vertex_shader = `
attribute vec4 vertex;
attribute vec2 textureCoordinates;
attribute vec4 normal;
uniform mat4 model_transform;
uniform mat4 vp_transform;
uniform mat4 normal_transform;
varying vec2 vTexCoords;
varying vec4 obj_loc;
varying vec4 transformed_normal;
varying vec4 vColor;
uniform vec4 light_position;
uniform vec4 light_ambient;
uniform vec4 light_diffuse;
uniform vec4 mat_ambient;
uniform vec4 mat_diffuse;
uniform vec4 mat_specular;
uniform float mat_shininess;
uniform vec4 light_specular;
uniform vec4 camera_position;
uniform int flags;
void main()
{
obj_loc = model_transform * vertex; // vertex in model coordinates
gl_Position = vp_transform * obj_loc; // vertex in device independent coordinates
transformed_normal = normal_transform * normal; // normal in model coordinates
vTexCoords = textureCoordinates;
vec4 l_vec = normalize(light_position - obj_loc);
vec4 n_vec = normalize(normal_transform * normal);
float lndot = dot(l_vec, n_vec);
float diffuse_scale = max(0.0, lndot);
vec4 diffuse_color = diffuse_scale * light_diffuse * mat_diffuse;
if( (flags - 2*(flags/2)) == 0)
diffuse_color = vec4(0.0, 0.0, 0.0, 1.0);
vec4 h_vec = normalize(l_vec + normalize(camera_position - obj_loc));
float spec_scale = pow(max(0.0, dot(h_vec, n_vec)), mat_shininess);
vec4 specular_color;
if(lndot < 0.0) {
specular_color = vec4(0.0, 0.0, 0.0, 1.0);
} else {
specular_color = spec_scale * mat_specular * light_specular;
}
if( (flags - 4*(flags/4)) < 2 ) {
specular_color = vec4(0.0, 0.0, 0.0, 1.0);
}
vec4 ambient_color = mat_ambient * light_ambient;
if(flags < 4) {
ambient_color = vec4(0.0, 0.0, 0.0, 1.0);
}
}`;
const fragment_shader = `
precision mediump float;
uniform vec4 light_position;
uniform vec4 light_ambient;
uniform vec4 light_diffuse;
varying vec2 vTexCoords;
varying vec4 obj_loc;
varying vec4 transformed_normal;
uniform sampler2D sampler;
void
main()
{
vec4 texture_sample = texture2D(sampler, vTexCoords);
vec4 l_vec = normalize(light_position - obj_loc);
vec4 n_vec = normalize(transformed_normal);
float lndot = dot(l_vec, n_vec);
float diffuse_scale = max(0.0, lndot);
vec4 diffuse_color = diffuse_scale * light_diffuse * texture_sample;
vec4 ambient_color = light_ambient * texture_sample;
gl_FragColor = diffuse_color + ambient_color;
gl_FragColor.a = 1.0;
}
`;
The variables I am trying to use between both shaders are light_position, light_ambient, and light_diffuse. How would I go about doing this?

The link error is caused by the different precision in the vertex shader and fragment shader. See 4.5 Precision and Precision Qualifiers
From OpenGL ES Shading Language 1.00 Specification - 4.5.3 Default Precision Qualifiers
The vertex language has the following predeclared globally scoped default precision statements:
precision highp float;
The fragment shader has no default precision for float, hence you have specified the default precision mediump:
precision mediump float;
Thus the precision of the floating point uniforms are different in the fragment shader and vertex shader.
Either set an explicit mediump precision for floats in the vertex shader:
const vertex_shader = `
precision mediump float;
...
or change the precision in the fragment shader from medium to high.
Alternatively you can set an explicite precision qualifier to the uniform variables which are used in the vertex shader and fragment shader. For instance:
uniform mediump vec4 light_position;

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!

Add two textures on the same mesh WEBGL

I need to add two textures on the same mesh. I have an octahedron with a texture and I want to add a second texture above it. How can I achieve this? The first texture is appearing but the second one not. I created two different textures at my .js file and enabled 2 different texture slots for both of them. I did bind the textures and pass the textures slots as a uniform in the fragment shader. I think that my HTML file is wrong so I am attaching the code of the HTML file.
<script id="shader-fs" type="x-shader/x-fragment">
precision mediump float;
varying vec2 vTextureCoord;
varying vec3 vLightWeighting;
uniform sampler2D uSampler;
uniform sampler2D uSampler1;
void main(void) {
vec3 c;
vec4 textureColor = texture2D(uSampler, vec2(vTextureCoord.s, vTextureCoord.t));
vec4 textureColor1 = texture2D(uSampler1, vec2(vTextureCoord.s, vTextureCoord.t));
c = textureColor.rgb * textureColor.a + textureColor1.rgb * textureColor1.a * (1.0 - textureColor.a);
gl_FragColor= vec4(c, 1.0);
}
</script>
<script id="shader-vs" type="x-shader/x-vertex">
attribute vec3 aVertexPosition;
attribute vec3 aVertexNormal;
attribute vec2 aTextureCoord;
uniform mat4 uMVMatrix;
uniform mat4 uPMatrix;
uniform mat3 uNMatrix;
uniform vec3 uLightingDirection;
uniform vec3 uDirectionalColor;
varying vec2 vTextureCoord;
varying vec3 vLightWeighting;
void main(void) {
gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 0.5);
vTextureCoord = aTextureCoord;
vec3 transformedNormal = uNMatrix * aVertexNormal;
float directionalLightWeighting = max(dot(transformedNormal, uLightingDirection), 0.0);
vLightWeighting = uDirectionalColor * directionalLightWeighting;
}
</script>

WebGL website looks fine on PC, but looks weird on mobile devices

So I made a WebGL website and it can be accessed here:
https://jameswebsite.azurewebsites.net/
It looks fine(or as expected) on the PC, but on mobile devices it looks funny. Looks like the texture mapping might be off (maybe texture clipping is the problem) but also there doesn't appear to be any shading occuring.
Here is the screenshot from the PC:
PC Image]1
Here is the screenshot from the Mobile Device:
Mobile Image
I have turned off the textures and still have this problem. This leads me to believe the problem could be in my shaders. Here are my shaders:
<script id="per-fragment-lighting-fs" type="x-shader/x-fragment">
precision mediump float;
varying vec2 vTextureCoord;
varying vec3 vTransformedNormal;
varying vec4 vPosition;
uniform float uMaterialShininess;
uniform bool uShowSpecularHighlights;
uniform bool uUseLighting;
uniform bool uUseTextures;
uniform vec3 uAmbientColor;
uniform vec3 uPointLightingLocation;
uniform vec3 uPointLightingSpecularColor;
uniform vec3 uPointLightingDiffuseColor;
uniform sampler2D uSampler;
void main(void) {
vec3 lightWeighting;
if (!uUseLighting) {
lightWeighting = vec3(1.0, 1.0, 1.0);
} else {
vec3 lightDirection = normalize(uPointLightingLocation - vPosition.xyz);
vec3 normal = normalize(vTransformedNormal);
float specularLightWeighting = 0.0;
if (uShowSpecularHighlights) {
vec3 eyeDirection = normalize(-vPosition.xyz);
vec3 reflectionDirection = reflect(-lightDirection, normal);
specularLightWeighting = pow(max(dot(reflectionDirection, eyeDirection), 0.0), uMaterialShininess);
}
float diffuseLightWeighting = max(dot(normal, lightDirection), 0.0);
lightWeighting = uAmbientColor
+ uPointLightingSpecularColor * specularLightWeighting
+ uPointLightingDiffuseColor * diffuseLightWeighting;
}
vec4 fragmentColor;
if (uUseTextures) {
fragmentColor = texture2D(uSampler, vec2(vTextureCoord.s, vTextureCoord.t));
} else {
fragmentColor = vec4(1.0, 1.0, 1.0, 1.0);
}
gl_FragColor = vec4(fragmentColor.rgb * lightWeighting, fragmentColor.a);
}
<script id="per-fragment-lighting-vs" type="x-shader/x-vertex">
attribute vec3 aVertexPosition;
attribute vec3 aVertexNormal;
attribute vec2 aTextureCoord;
uniform mat4 uMVMatrix;
uniform mat4 uPMatrix;
uniform mat3 uNMatrix;
varying vec2 vTextureCoord;
varying vec3 vTransformedNormal;
varying vec4 vPosition;
void main(void) {
vPosition = uMVMatrix * vec4(aVertexPosition, 1.0);
gl_Position = uPMatrix * vPosition;
vTextureCoord = aTextureCoord;
vTransformedNormal = uNMatrix * aVertexNormal;
}
Any ideas? Thanks in Advance!
On mobile devices, WebGL implementations can be sensitive to things like non-base two texture dimensions, or can require texture wrapping behaviour to be explicitly set.
I noticed your grass texture has dimensions of 590x590. Consider resizing your textures to the closes base two dimensions (ie in the case of your grass texture, 512x512)
Also, I would recommend you explicity set the gl.TEXTURE_WRAP_S and gl.TEXTURE_WRAP_T parameters on your texture object(s) to gl.GL_REPEAT, as this may be another cause for your textures only partially displaying on geometry.
You can set wrapping behaviour on a texture object in the following way:
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
Per #gman:
change mediump to highp in your shaders and see what that does
This actually worked!! Thanks #gman!

Are uniforms actually defined in THREE.js RawShaderMaterial?

The documentation for the THREE.Js RawShaderMaterial says:
built-in uniforms and attributes are not automatically prepended to the GLSL shader code.
However, in practice I am able to run the following shader, with a rawShaderMaterial:
<script type="x-shader/x-vertex" id="vertexShader">
precision mediump float;
precision mediump int;
uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;
attribute vec3 position;
attribute vec4 color;
varying vec3 vPosition;
varying vec4 vColor;
void main() {
vPosition = position;
vColor = color;
gl_Position = projectionMatrix *
modelViewMatrix *
vec4(position.x, position.y, position.z, 1.0);
}
Without defining the modelViewMatrix or projectionMatrix anywhere. Are some uniforms actually are passed to the rawshader material after all?
Yes,
uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;
uniform mat4 modelMatrix;
uniform mat4 viewMatrix;
uniform mat3 normalMatrix;
uniform vec3 cameraPosition;
are available to you when using RawShaderMaterial, however you must declare them in your shader if you want to use them. They are not automatically prefixed.
three.js r.73

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