I hope someone can help me with this. I am at the end of my rope, having gone through all of the discussions and examples I have found and still can’t get dFdx working, neither for WebGl1 or WebGL2. Partial code is shown below.
Thanks for you help.
The fragment shader uses:
#extension GL_OES_standard_derivatives : enable
precision highp float;
varying vec3 vNormal;
varying vec2 vUv;
varying vec3 vViewPosition;
uniform vec3 color;
uniform float animateRadius;
uniform float animateStrength;
vec3 faceNormal(vec3 pos) {
vec3 fdx = vec3(dFdx(pos.x), dFdx(pos.y), dFdx(pos.z));
vec3 fdy = vec3(dFdy(pos.x), dFdy(pos.y), dFdy(pos.z));
//vec3 fdx = dFdx(pos);
//vec3 fdy = dFdy(pos);
return normalize(cross(fdx, fdy));
}
The console shows the following:
THREE.WebGLProgram: shader error: 0 35715 false gl.getProgramInfoLog Must have an compiled fragment shader attached. <empty string> THREE.WebGLShader: gl.getShaderInfoLog() fragment
WARNING: 0:2: 'GL_OES_standard_derivatives' : extension is not supported
ERROR: 0:14: 'GL_OES_standard_derivatives' : extension is not supported
ERROR: 0:14: 'GL_OES_standard_derivatives' : extension is not supported
ERROR: 0:14: 'GL_OES_standard_derivatives' : extension is not supported
ERROR: 0:15: 'GL_OES_standard_derivatives' : extension is not supported
ERROR: 0:15: 'GL_OES_standard_derivatives' : extension is not supported
ERROR: 0:15: 'GL_OES_standard_derivatives' : extension is not supported1: #define lengthSegments 300.0
2: #extension GL_OES_standard_derivatives : enable
3: precision highp float;
4:
5: varying vec3 vNormal;
6: varying vec2 vUv;
7: varying vec3 vViewPosition;
Here is also part of the Javascript:
module.exports = function (app) {
const totalMeshes = isMobile ? 30 : 40;
const isSquare = false;
const subdivisions = isMobile ? 200 : 300;
const numSides = isSquare ? 4 : 8;
const openEnded = false;
const geometry = createTubeGeometry(numSides, subdivisions, openEnded);
// add to a parent container
const container = new THREE.Object3D();
const lines = [];
//lines.length = 0;
ShaderLoader("scripts/Grp3D/Phys4646A2/tube.vert", "scripts/Grp3D/Phys4646A2/tube.frag", function (vertex, fragment) {
const baseMaterial = new THREE.RawShaderMaterial({
vertexShader: vertex,
fragmentShader: fragment,
side: THREE.FrontSide,
extensions: {
derivatives: true
},
defines: {
lengthSegments: subdivisions.toFixed(1),
ROBUST: false,
ROBUST_NORMALS: false,
FLAT_SHADED: isSquare
},
uniforms: {
thickness: { type: 'f', value: 1 },
time: { type: 'f', value: 0 },
color: { type: 'c', value: new THREE.Color('#303030') },
animateRadius: { type: 'f', value: 0 },
animateStrength: { type: 'f', value: 0 },
index: { type: 'f', value: 0 },
totalMeshes: { type: 'f', value: totalMeshes },
radialSegments: { type: 'f', value: numSides },
wavelength: { type: 'f', value: 2.0 }
}
});
for( var i = 0; i < totalMeshes; i++){
var t = totalMeshes <= 1 ? 0 : i / (totalMeshes - 1);
var material = baseMaterial.clone();
material.uniforms = THREE.UniformsUtils.clone(material.uniforms);
material.uniforms.index.value = t;
material.uniforms.thickness.value = randomFloat(0.005, 0.0075);
material.uniforms.wavelength.value = 2.0;
var mesh = new THREE.Mesh(geometry, material);
mesh.frustumCulled = false;
lines.push(mesh);
container.add(mesh);
}
});
return {
object3d: container,
update,
setPalette
};
function update (dt,wvl) {
dt = dt / 1000;
lines.forEach(mesh => {
//console.log(dt);
mesh.material.uniforms.time.value += dt;
mesh.material.uniforms.wavelength.value = wvl;
});
}
};
...
In WebGL2 with version 300 es shaders they are supported by default, no extension needed.
<canvas></canvas>
<script type="module">
import * as THREE from 'https://threejsfundamentals.org/threejs/resources/threejs/r122/build/three.module.js';
const canvas = document.querySelector('canvas');
const renderer = new THREE.WebGLRenderer({canvas});
const material = new THREE.RawShaderMaterial({
vertexShader: `#version 300 es
in vec4 position;
out vec4 vPosition;
uniform mat4 projectionMatrix;
uniform mat4 modelViewMatrix;
void main() {
gl_Position = projectionMatrix * modelViewMatrix * position;
vPosition = gl_Position;
}
`,
fragmentShader: `#version 300 es
precision mediump float;
in vec4 vPosition;
out vec4 outColor;
void main() {
outColor = vec4(normalize(vec3(dFdx(vPosition.x), dFdy(vPosition.y), 0)) * 0.5 + 0.5, 1);
}
`,
});
const geo = new THREE.BoxBufferGeometry();
const mesh = new THREE.Mesh(geo, material);
const scene = new THREE.Scene();
scene.add(mesh);
mesh.rotation.set(0.4, 0.4, 0);
const camera = new THREE.PerspectiveCamera(45, 2, 0.1, 10);
camera.position.z = 3;
renderer.render(scene, camera);
</script>
Note:
const fs = `#version 300 es
...
`;
Has #version 300 es
as the first line
const fs = `
#version 300 es
...
`;
Has #version 300 es
as the 2nd line (error)
With WebGL1 your code should work but three.js auto-chooses WebGL2 if it exists. To test WebGL1 we could force it to WebGL1 by creating the WebGL context ourselves,
const canvas = document.querySelector(selectorForCanvas);
const context = canvas.getContext('webgl');
const renderer = new THREE.WebGLRenderer({canvas, context});
Or, we can use a helper script to effectively disable webgl2
<script src="https://greggman.github.io/webgl-helpers/webgl2-disable.js"></script>
<canvas></canvas>
<script type="module">
import * as THREE from 'https://threejsfundamentals.org/threejs/resources/threejs/r122/build/three.module.js';
const canvas = document.querySelector('canvas');
const renderer = new THREE.WebGLRenderer({canvas});
const material = new THREE.RawShaderMaterial({
vertexShader: `
attribute vec4 position;
varying vec4 vPosition;
uniform mat4 projectionMatrix;
uniform mat4 modelViewMatrix;
void main() {
gl_Position = projectionMatrix * modelViewMatrix * position;
vPosition = gl_Position;
}
`,
fragmentShader: `
#extension GL_OES_standard_derivatives : enable
precision mediump float;
varying vec4 vPosition;
void main() {
gl_FragColor = vec4(normalize(vec3(dFdx(vPosition.x), dFdy(vPosition.y), 0)) * 0.5 + 0.5, 1);
}
`,
});
const geo = new THREE.BoxBufferGeometry();
const mesh = new THREE.Mesh(geo, material);
const scene = new THREE.Scene();
scene.add(mesh);
mesh.rotation.set(0.4, 0.4, 0);
const camera = new THREE.PerspectiveCamera(45, 2, 0.1, 10);
camera.position.z = 3;
renderer.render(scene, camera);
</script>
Otherwise you can look at renderer.capabilities.isWebGL2
to see if three.js choose WebGL2 and adjust your shaders as appropriate.
As for why your code used to work and doesn't work now, two ideas: