如何通过制服将鼠标位置从JS传递到着色器?

时间:2019-04-25 13:34:19

标签: three.js shader fragment-shader

我想通过统一的方式将鼠标位置传递给着色器,以便在移动鼠标时颜色可以交互改变。

请查看带有“★”的注释,我想这是我的问题的关键代码。

我对着色器很陌生,我只是出于锻炼目的而创建它。 我想知道的是如何将鼠标位置从js(我正在使用three.js)传递给着色器,以及如何在着色器中使用它。 (我相信需要对其进行规范化才能使用r,g和b中的值?)

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">

    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/104/three.min.js"></script>
    <title>Document</title>
</head>

<body>

    <div id="container"></div>

    <script id="vertexShader" type="x-shader/x-vertex">
    void main(){
        gl_Position = vec4(position, 1.0);
    }
    </script>

    <script id="fragmentShader" type="x-shader/x-fragment">
        uniform vec2 u_resolution;
        uniform float u_time;
        uniform vec2 u_mouse;


      //★set the mouse positions to the colors    
        void main(){
            vec2 st = gl_FragCoord.xy / u_resolution.xy;
            vec2 um = u_mouse.xy;

            gl_FragColor = vec4(um.x, um.y, 0.0, 0.1);
        } 

    </script>

    <script>
        var container;
        var camera, scene, renderer;
        var uniforms;

        init();
        animate();

        function init() {
            container = document.getElementById('container');

            camera = new THREE.Camera();
            camera.position.z = 1;

            scene = new THREE.Scene();

            var geometry = new THREE.PlaneBufferGeometry(2, 2);

            uniforms = {

                u_time: { type: "f", value: 1.0 },
                u_resolution: { type: "v2", value: new THREE.Vector2() },
                u_mouse: { type: "v2", value: new THREE.Vector2() }
            };

            var material = new THREE.ShaderMaterial({
                uniforms: uniforms,
                vertexShader: document.getElementById('vertexShader').textContent,
                fragmentShader: document.getElementById('fragmentShader').textContent
            });

            var mesh = new THREE.Mesh(geometry, material);
            scene.add(mesh);

            renderer = new THREE.WebGLRenderer();
            renderer.setPixelRatio(window.devicePixelRatio);

            container.appendChild(renderer.domElement);

            onWindowResize();
            window.addEventListener('resize', onWindowResize, false);


        }

        function onWindowResize(e) {
            renderer.setSize(window.innerWidth, window.innerHeight);
            uniforms.u_resolution.value.x = renderer.domElement.width;
            uniforms.u_resolution.value.y = renderer.domElement.height;
        }

        function animate() {
            requestAnimationFrame(animate);
            render();
        }

      //★set the mouse positions to shader
        function render() {
            uniforms.u_time.value += 0.05;
            document.onmousemove = function (e) {
                uniforms.u_mouse.x = e.pageX;
                uniforms.u_mouse.y = (e.pageY;

            }
            renderer.render(scene, camera);
        }

    </script>

</body>

</html>

1 个答案:

答案 0 :(得分:0)

首先,请学习如何使用snippet来解决其他问题。有一个很好的例子here

第二个问题的答案就在您的问题中。可以看到

uniforms.u_resolution.value.x = renderer.domElement.width;
uniforms.u_resolution.value.y = renderer.domElement.height;

还有这个?

uniforms.u_mouse.x = e.pageX;
uniforms.u_mouse.y = e.pageY;

Three.js制服的形式为uniforms.<name>.value.property。您没有放入“价值”部分。

尽管有其他问题,Three.js中的颜色从0变为1,所以仅使用e.pageXe.pageY不会为您提供任何颜色,除了页面左上角的黑色和其他地方都是白色,因为其他地方的值都将为1或更大

您可以尝试

uniforms.u_mouse.x = e.pageX / window.innerWidth;
uniforms.u_mouse.y = e.pageY / window.innerHeight;

也许给您一个更合适的价值。我说也许是因为不清楚您要在哪个上下文中更改颜色。在窗口上方,页面上方,画布上方,其他元素上方,所有这些都可以不同。

另一件事,您通常不希望在渲染循环内分配事件。您可以在初始化时分配一次。

这是代码段中的代码

var container;
var camera, scene, renderer;
var uniforms;

init();
animate();

function init() {
  container = document.getElementById('container');

  camera = new THREE.Camera();
  camera.position.z = 1;

  scene = new THREE.Scene();

  var geometry = new THREE.PlaneBufferGeometry(2, 2);

  uniforms = {

    u_time: {
      type: "f",
      value: 1.0
    },
    u_resolution: {
      type: "v2",
      value: new THREE.Vector2()
    },
    u_mouse: {
      type: "v2",
      value: new THREE.Vector2()
    }
  };

  var material = new THREE.ShaderMaterial({
    uniforms: uniforms,
    vertexShader: document.getElementById('vertexShader').textContent,
    fragmentShader: document.getElementById('fragmentShader').textContent
  });

  var mesh = new THREE.Mesh(geometry, material);
  scene.add(mesh);

  renderer = new THREE.WebGLRenderer();
  renderer.setPixelRatio(window.devicePixelRatio);

  container.appendChild(renderer.domElement);

  onWindowResize();
  window.addEventListener('resize', onWindowResize, false);


}

function onWindowResize(e) {
  renderer.setSize(window.innerWidth, window.innerHeight);
  uniforms.u_resolution.value.x = renderer.domElement.width;
  uniforms.u_resolution.value.y = renderer.domElement.height;
}

function animate() {
  requestAnimationFrame(animate);
  render();
}

document.onmousemove = function(e) {
  uniforms.u_mouse.value.x = e.pageX / window.innerWidth;
  uniforms.u_mouse.value.y = e.pageY / window.innerHeight;
}


//★set the mouse positions to shader
function render() {
  uniforms.u_time.value += 0.05;
    renderer.render(scene, camera);
  }
body { margin: 0; }
canvas { display: block; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/104/three.min.js"></script>

<div id="container"></div>

<script id="vertexShader" type="x-shader/x-vertex">
  void main(){
    gl_Position = vec4(position, 1.0);
  }
</script>

<script id="fragmentShader" type="x-shader/x-fragment">
  uniform vec2 u_resolution;
  uniform float u_time;
  uniform vec2 u_mouse;
  //set the mouse positions to the colors
  void main(){
    vec2 st = gl_FragCoord.xy / u_resolution.xy;
    vec2 um = u_mouse.xy;
    gl_FragColor = vec4(um.x, um.y, 0.0, 0.1);
  }
</script>