Я не могу заставить производные (dFdx) работать в моем шейдере с WebGL1 или 2

Я надеюсь, что кто-то может помочь мне с этим. Я нахожусь в конце своей веревки, пройдя все обсуждения и примеры, которые я нашел, и все еще не могу заставить работать dFdx ни для WebGl1, ни для WebGL2. Частичный код показан ниже.

Спасибо за помощь.

Фрагментный шейдер использует:

#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));
}

Консоль показывает следующее:

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;

Вот также часть 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.info(dt);
        mesh.material.uniforms.time.value += dt;
        mesh.material.uniforms.wavelength.value = wvl;
    });
    }
}; 

...

Если расширение не поддерживается, вы не можете сделать так, чтобы оно поддерживалось.

Nicol Bolas 21.12.2020 06:25

Вы сказали, что пробовали WebGL 2, это будет означать, что у вас есть поддержка WebGL 2, и в этом случае вы сможете просто удалить оператор #extension и просто использовать производные функции, установив версию шейдера с помощью оператора #version es 3.0.

LJᛃ 21.12.2020 23:57

@Nicol: всего несколько месяцев назад это работало нормально (это означает, что ошибка не появлялась). Я не знаю, что могло измениться (я использую Firefox, но также пробовал Chrome).

Michael Gericke 22.12.2020 04:43

@LJ: Да, я пробовал. Он жалуется, что #version es 3.0 не является первой строкой в ​​шейдере, хотя это так.

Michael Gericke 22.12.2020 04:44
Почему в Python есть оператор &quot;pass&quot;?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
Массив зависимостей в React
Массив зависимостей в React
Все о массиве Dependency и его связи с useEffect.
0
4
1 469
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

В WebGL2 с шейдерами версии 300 они поддерживаются по умолчанию, расширения не требуются.

<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>

Примечание:

const fs = `#version 300 es
...
`;

Имеет #version 300 es в качестве первой строки

const fs = `
#version 300 es
...
`;

Имеет #version 300 es во 2-й строке (ошибка)

С WebGL1 ваш код должен работать, но three.js автоматически выбирает WebGL2, если он существует. Чтобы протестировать WebGL1, мы могли бы заставить его использовать WebGL1, самостоятельно создав контекст WebGL,

const canvas = document.querySelector(selectorForCanvas);
const context = canvas.getContext('webgl');
const renderer = new THREE.WebGLRenderer({canvas, context});

Или мы можем использовать вспомогательный скрипт для эффективного отключения 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>

В противном случае вы можете посмотреть renderer.capabilities.isWebGL2, чтобы узнать, выбирает ли three.js WebGL2, и соответствующим образом настроить свои шейдеры.

Что касается того, почему ваш код работал и не работает сейчас, две идеи:

  1. ваша машина раньше не поддерживала WebGL2, а теперь поддерживает
  2. вы обновили three.js до версии, которая автоматически выбирает WebGL2, а предыдущая версия, которую вы использовали, этого не делала.

Спасибо. Похоже, что getContext возвращает ненулевое значение только для WebGL2. Я не знаю, как заставить webgl 1. Я не помню, чтобы менял версию three.js, но было обновление браузера и, возможно, было недавнее обновление аппаратного диска (насколько я помню). Могло ли это быть причиной? Мне пришлось внести одно дополнительное изменение, чтобы заставить его работать: ошибка, связанная с оператором #version, отсутствующим в первой строке, была связана с оператором определения lengthSegments: subdivisions.toFixed(1), в вызове RawShaderMaterial. Мне пришлось переместить это в сам код шейдера.

Michael Gericke 22.12.2020 07:04

Я думаю, последний вопрос: что происходит, когда кто-то вызывает веб-сайт с шейдером, написанным для webgl 2, но машина этого человека поддерживает только webgl 1? Но я полагаю, это другая тема.

Michael Gericke 22.12.2020 07:16

Это терпит неудачу. Как сказано внизу моего ответа, вы можете проверить флаг и настроить свои шейдеры. Вы можете получить некоторое представление о том, как three.js делает это для встроенных шейдеров здесь

gman 22.12.2020 07:31

@Marquizzo, это уже есть в ответе;)

gman 22.12.2020 11:43

Другие вопросы по теме