Three.js основы, например, shadertoy, как преобразовать шаблон вместе с объектом?

#javascript #html #three.js #webgl

#javascript #HTML #three.js #webgl

Вопрос:

Вот пример, основанный на втором живом примере из https://threejsfundamentals.org/threejs/lessons/threejs-shadertoy.html:

 html, body {
  height: 100%;
  margin: 0;
}
#c {
  width: 100%;
  height: 100%;
  display: block;
} 
 <canvas id="c"></canvas>
  
<script type="module">
// Three.js - Shadertoy Basic
// from https://threejsfundamentals.org/threejs/threejs-shadertoy-basic.html


import * as THREE from 'https://unpkg.com/three@0.122.0/build/three.module.js';

function main() {
  const canvas = document.querySelector('#c');
  const renderer = new THREE.WebGLRenderer({canvas});
  renderer.autoClearColor = false;

  const camera = new THREE.PerspectiveCamera(
    45, 16/9, 0.01, 1000
  );
  camera.position.z = 5
  const scene = new THREE.Scene();
  const plane = new THREE.PlaneBufferGeometry(2, 2);


  const fragmentShader = `
  #include <common>

  uniform vec3 iResolution;
  uniform float iTime;

  // https://www.shadertoy.com/view/MtXSWj

  float alternate(float p, float d){;
    return sign(fract(p*d*.5)*2.-1.);
  }

  vec3 rainbow(float t){
      return sin(t vec3(0,.33,.66)*6.28)*.5 .5;
  }

  vec3 TwinDragon(vec2 p){
      float time       = fract(iTime*0.05)*20.;
      
      //scaling
      p = (p*2.-iResolution.xy)/iResolution.y*1.5;
      
      //----------the fractal stuff----   ---THIS IS ANIMATIONS----(so remove them if you want)
      p.y  = alternate(p.x, 256. )/512. * clamp(time-16.,0.,2.)/2.;
      p.x -= alternate(p.y, 128. )/256. * clamp(time-14.,0.,2.)/2.;
      p.y  = alternate(p.x,  64. )/128. * clamp(time-12.,0.,2.)/2.;
      p.x -= alternate(p.y,  32. )/ 64. * clamp(time-10.,0.,2.)/2.;
      p.y  = alternate(p.x,  16. )/ 32. * clamp(time- 8.,0.,2.)/2.;
      p.x -= alternate(p.y,   8. )/ 16. * clamp(time- 6.,0.,2.)/2.;
      p.y  = alternate(p.x,   4. )/  8. * clamp(time- 4.,0.,2.)/2.;
      p.x -= alternate(p.y,   2. )/  4. * clamp(time- 2.,0.,2.)/2.;

      // prettifying
      vec2  block  = ceil(p .5);               //index for blocks from which the fractal is shifted
      vec3  color  = rainbow(block.x*4. block.y);  //rainbow palette using block index as t
      float dis    = length(fract(p .5)*2.-1.);//distance to middle of block
            color *= .5 dis*.7;                    //using distance within block for some more pretty.
      
      return color;
  }

  void mainImage( out vec4 fragColor, in vec2 fragCoord ){
      
      vec2 d = vec2(.5,0);
      
      //some antialiasing
      vec3 col = (
          TwinDragon(fragCoord d.xy) 
          TwinDragon(fragCoord-d.xy) 
          TwinDragon(fragCoord d.yx) 
          TwinDragon(fragCoord-d.yx)
      )*.25;
      
    fragColor = vec4(col,1.);
      
  }

  void main() {
    mainImage(gl_FragColor, gl_FragCoord.xy);
  }
  `;
  const uniforms = {
    iTime: { value: 0 },
    iResolution:  { value: new THREE.Vector3() },
  };
  const material = new THREE.ShaderMaterial({
    fragmentShader,
    uniforms,
    side: THREE.DoubleSide
  });
  const mesh = new THREE.Mesh(plane, material)
  scene.add(mesh);

  function resizeRendererToDisplaySize(renderer) {
    const canvas = renderer.domElement;
    const width = canvas.clientWidth;
    const height = canvas.clientHeight;
    const needResize = canvas.width !== width || canvas.height !== height;
    if (needResize) {
      renderer.setSize(width, height, false);
    }
    return needResize;
  }

  function render(time) {
    time *= 0.001;  // convert to seconds



    resizeRendererToDisplaySize(renderer);

    const canvas = renderer.domElement;
    uniforms.iResolution.value.set(canvas.width, canvas.height, 1);
    uniforms.iTime.value = time;

    mesh.rotation.y  = 0.01

    renderer.render(scene, camera);

    requestAnimationFrame(render);
  }

  requestAnimationFrame(render);
}

main();

</script> 

Но, как вы видите, когда я переключаюсь на PerspectiveCamera и поворачиваю плоскость, текстура не преобразуется вместе с объектом.

Какой самый простой способ изменить пример, чтобы преобразовать шейдер вместе с плоскостью, чтобы изображение отображалось на поверхности плоскости, а не на плоскости, похожей на маску?

Комментарии:

1. Разве этот пример не находится дальше на странице, на которую вы ссылаетесь? Он показывает сопоставление шейдера shadertoy с кубом с использованием uv-координат

2. @gman Действительно! Но мне потребовалось некоторое время, чтобы понять, в чем разница. Полный новичок.

Ответ №1:

Ответ заключается в передаче uv-координат из вершинного шейдера в фрагментный шейдер с использованием varying переменной.

В принципе, мы можем заменить строки

   const fragment = `
  ... clipped ...
  void main() {
    mainImage(gl_FragColor, gl_FragCoord.xy);
  }
  `;
  const uniforms = {
    iTime: { value: 0 },
    iResolution:  { value: new THREE.Vector3() },
  };
  const material = new THREE.ShaderMaterial({
    fragmentShader,
 

с помощью

   const fragment = `
  ... clipped ...
  varying vec2 vUv;

  void main() {
    mainImage(gl_FragColor, vUv * iResolution.xy);
  }
  `;
  const vertexShader = `
    varying vec2 vUv;
    void main() {
      vUv = uv;
      gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
    }
  `;
  const uniforms = {
    iTime: { value: 0 },
    iResolution:  { value: new THREE.Vector3() },
  };
  const material = new THREE.ShaderMaterial({
    vertexShader,
    fragmentShader,
 

и мы получаем следующий результат:

 html, body {
  height: 100%;
  margin: 0;
}
#c {
  width: 100%;
  height: 100%;
  display: block;
} 
 <canvas id="c"></canvas>
  
<script type="module">
// Three.js - Shadertoy Basic
// from https://threejsfundamentals.org/threejs/threejs-shadertoy-basic.html


import * as THREE from 'https://unpkg.com/three@0.122.0/build/three.module.js';

function main() {
  const canvas = document.querySelector('#c');
  const renderer = new THREE.WebGLRenderer({canvas});
  renderer.autoClearColor = false;

  const camera = new THREE.PerspectiveCamera(
    45, 16/9, 0.01, 1000
  );
  camera.position.z = 5
  const scene = new THREE.Scene();
  const plane = new THREE.PlaneBufferGeometry(2, 2);


  const fragmentShader = `
  #include <common>

  uniform vec3 iResolution;
  uniform float iTime;

  // https://www.shadertoy.com/view/MtXSWj

  float alternate(float p, float d){;
    return sign(fract(p*d*.5)*2.-1.);
  }

  vec3 rainbow(float t){
      return sin(t vec3(0,.33,.66)*6.28)*.5 .5;
  }

  vec3 TwinDragon(vec2 p){
      float time       = fract(iTime*0.05)*20.;
      
      //scaling
      p = (p*2.-iResolution.xy)/iResolution.y*1.5;
      
      //----------the fractal stuff----   ---THIS IS ANIMATIONS----(so remove them if you want)
      p.y  = alternate(p.x, 256. )/512. * clamp(time-16.,0.,2.)/2.;
      p.x -= alternate(p.y, 128. )/256. * clamp(time-14.,0.,2.)/2.;
      p.y  = alternate(p.x,  64. )/128. * clamp(time-12.,0.,2.)/2.;
      p.x -= alternate(p.y,  32. )/ 64. * clamp(time-10.,0.,2.)/2.;
      p.y  = alternate(p.x,  16. )/ 32. * clamp(time- 8.,0.,2.)/2.;
      p.x -= alternate(p.y,   8. )/ 16. * clamp(time- 6.,0.,2.)/2.;
      p.y  = alternate(p.x,   4. )/  8. * clamp(time- 4.,0.,2.)/2.;
      p.x -= alternate(p.y,   2. )/  4. * clamp(time- 2.,0.,2.)/2.;

      // prettifying
      vec2  block  = ceil(p .5);               //index for blocks from which the fractal is shifted
      vec3  color  = rainbow(block.x*4. block.y);  //rainbow palette using block index as t
      float dis    = length(fract(p .5)*2.-1.);//distance to middle of block
            color *= .5 dis*.7;                    //using distance within block for some more pretty.
      
      return color;
  }

  void mainImage( out vec4 fragColor, in vec2 fragCoord ){
      
      vec2 d = vec2(.5,0);
      
      //some antialiasing
      vec3 col = (
          TwinDragon(fragCoord d.xy) 
          TwinDragon(fragCoord-d.xy) 
          TwinDragon(fragCoord d.yx) 
          TwinDragon(fragCoord-d.yx)
      )*.25;
      
    fragColor = vec4(col,1.);
      
  }

  varying vec2 vUv;

  void main() {
    mainImage(gl_FragColor, vUv * iResolution.xy);
  }
  `;
  const vertexShader = `
    varying vec2 vUv;
    void main() {
      vUv = uv;
      gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
    }
  `;
  const uniforms = {
    iTime: { value: 0 },
    iResolution:  { value: new THREE.Vector3() },
  };
  const material = new THREE.ShaderMaterial({
    vertexShader,
    fragmentShader,
    uniforms,
    side: THREE.DoubleSide
  });
  const mesh = new THREE.Mesh(plane, material)
  scene.add(mesh);

  function resizeRendererToDisplaySize(renderer) {
    const canvas = renderer.domElement;
    const width = canvas.clientWidth;
    const height = canvas.clientHeight;
    const needResize = canvas.width !== width || canvas.height !== height;
    if (needResize) {
      renderer.setSize(width, height, false);
    }
    return needResize;
  }

  function render(time) {
    time *= 0.001;  // convert to seconds



    resizeRendererToDisplaySize(renderer);

    const canvas = renderer.domElement;
    uniforms.iResolution.value.set(canvas.width, canvas.height, 1);
    uniforms.iTime.value = time;

    mesh.rotation.y  = 0.01

    renderer.render(scene, camera);

    requestAnimationFrame(render);
  }

  requestAnimationFrame(render);
}

main();

</script>