threejs shader 入门(四)

279 阅读1分钟

没看过的朋友,记得先去看下入门一。

基础代码

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

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <style>
        * {
            margin: 0;
            padding: 0;
        }

        canvas {
            width: 100%;
            height: 100%
        }
    </style>
</head>

<body>
    
</body>
<script src="./three.js">
</script>
<script> 
   const vshader = `
        varying vec2 v_uv;
        void main() {	
            v_uv = uv;
            gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
        }`
    const fshader = ` 
       
        void main(){
            gl_FragColor=vec4(1.0,0.0,0.0,1.0);
         }
        `;
    const scene = new THREE.Scene();
    const camera = new THREE.PerspectiveCamera(75, window.innerWidth/ window.innerHeight, 0.1, 1000);
    const renderer = new THREE.WebGLRenderer();
    renderer.setSize(window.innerWidth, window.innerHeight);
    document.body.appendChild(renderer.domElement);
    const uniforms = {
       
    }
    const geometry = new THREE.PlaneGeometry(2, 2);
    const material = new THREE.ShaderMaterial({
        vertexShader: vshader,
        fragmentShader: fshader,
        uniforms
    });

    const plane = new THREE.Mesh(geometry, material);
    scene.add(plane);

    camera.position.z = 3;
    
    onWindowResize();
   
    animate();
    window.addEventListener("resize", onWindowResize, false);
    function onWindowResize(event) {
        camera.aspect =  window.innerWidth/ window.innerHeight
       camera.updateProjectionMatrix();
       renderer.setSize( window.innerWidth,window.innerHeight);
    }
    function animate() {
        requestAnimationFrame(animate);
        renderer.render(scene, camera);
    }
</script>

</html>

在平面上加载图片。

 const fshader = ` 
        uniform sampler2D u_tex;
        varying vec2 v_uv;
        void main(){
            vec3 color =texture2D(u_tex,v_uv ).rgb;
            gl_FragColor=vec4(color,1.0);
         }
        `;
 const uniforms = {
        u_tex: { value: new THREE.TextureLoader().load("./target.png") }
    }  

之前一直用的正交相机,之后都用透视相机了。 1.png

sampler2D 2D纹理,仅能作为uniform变量,一般就是你传的图片。

texture2D 参数有很多钟,目前使用的参数为 sampler2D 和 uv。[0,0]和[1,1]对应的颜色值及从左下角的颜色到右上角颜色。返回vec4.

我使用的图片是1200、741.图片撑满平面导致被拉伸了,我想要图片不被拉伸。且多余的平面不展示。

const fshader = ` 
        uniform sampler2D u_tex;
        varying vec2 v_uv;
        const float aspect = 1200.0/741.0;
        void main(){
            
            vec2 uv= vec2(v_uv.x,v_uv.y*aspect);
            if(uv.y > 1.0){
                discard;
            }
            vec3 color =texture2D(u_tex,uv ).rgb;
            gl_FragColor=vec4(color,1.0);
         }
        `;

2.png

discard 丢弃片段,直接不渲染你不需要的点。

做一个图片切换的效果。

const fshader = ` 
        uniform sampler2D u_tex;
        uniform sampler2D u_tex2;
        uniform float rate;
        varying vec2 v_uv;

        void main(){
            // vec3 color1 =texture2D(u_tex,v_uv ).rgb;
            // vec3 color2 =texture2D(u_tex2,v_uv ).rgb;
            vec4 color = mix(texture2D(u_tex,v_uv ),texture2D(u_tex2,v_uv ),rate);
            gl_FragColor=vec4(color.rgb,1.0);
         }
        `;
        
        
        
 const uniforms = {
        u_tex: { value: new THREE.TextureLoader().load("./target.png") },
        u_tex2: { value: new THREE.TextureLoader().load("./1.jpg") },
        rate:{value:0}
    }
      
      
      
      
 let speed =0.005;
    
    
    
    
 function animate() {
        requestAnimationFrame(animate);
        uniforms.rate.value+=speed;
        
        if(uniforms.rate.value>1 || uniforms.rate.value<0){
            speed *=-1
        }
        renderer.render(scene, camera);
    }

1.gif

mix(a,b,c) = a*(1-c)+b*c;

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

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <style>
        * {
            margin: 0;
            padding: 0;
        }

        canvas {
            width: 100%;
            height: 100%
        }
    </style>
</head>

<body>
    
</body>
<script src="./three.js">
</script>
<script src="./OrbitControls.js">
</script>
<script> 
   const vshader = `
        varying vec2 v_uv;
        void main() {	
            v_uv = uv;
            gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
        }`
    const fshader = ` 
        varying vec2 v_uv;
        void main(){
            gl_FragColor=vec4(v_uv,0.0,1.0);
         }
        `;
    const scene = new THREE.Scene();
    const camera = new THREE.PerspectiveCamera(75, window.innerWidth/ window.innerHeight, 0.1, 1000);
    const renderer = new THREE.WebGLRenderer();
    renderer.setSize(window.innerWidth, window.innerHeight);
    document.body.appendChild(renderer.domElement);
    const uniforms = {
       
    }
    const geometry = new THREE.BoxGeometry( 1, 1, 1 );
    const material = new THREE.ShaderMaterial({
        vertexShader: vshader,
        fragmentShader: fshader,
        uniforms
    });
    const controls = new THREE.OrbitControls(camera, renderer.domElement);
    const plane = new THREE.Mesh(geometry, material);
    scene.add(plane);

    camera.position.z = 3;
    
    onWindowResize();
   
    animate();
    window.addEventListener("resize", onWindowResize, false);
    function onWindowResize(event) {
        camera.aspect =  window.innerWidth/ window.innerHeight
       camera.updateProjectionMatrix();
       renderer.setSize( window.innerWidth,window.innerHeight);
    }
    
    function animate() {
        requestAnimationFrame(animate);
        
        renderer.render(scene, camera);
    }
</script>

</html>

代码重置,加入轨道控制器。

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

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <style>
        * {
            margin: 0;
            padding: 0;
        }

        canvas {
            width: 100%;
            height: 100%
        }
    </style>
</head>

<body>
    
</body>
<script src="./three.js">
</script>
<script src="./OrbitControls.js">
</script>
<script> 
   const vshader = `
        varying vec2 v_uv;
        void main() {	
            v_uv = uv;
            gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
        }`
    const fshader = ` 
        varying vec2 v_uv;
        void main(){
            gl_FragColor=vec4(v_uv.x,0.0,0.0,1.0);
         }
        `;
    const scene = new THREE.Scene();
    const camera = new THREE.PerspectiveCamera(75, window.innerWidth/ window.innerHeight, 0.1, 1000);
    const renderer = new THREE.WebGLRenderer();
    renderer.setSize(window.innerWidth, window.innerHeight);
    document.body.appendChild(renderer.domElement);
    const uniforms = {
    }
    const geometry = new THREE.CylinderGeometry( 5, 5, 20, 4,1,true );
    const material = new THREE.ShaderMaterial({
        vertexShader: vshader,
        fragmentShader: fshader,
        uniforms,
        side: THREE.DoubleSide,
    });
    const controls = new THREE.OrbitControls(camera, renderer.domElement);
    const plane = new THREE.Mesh(geometry, material);
    scene.add(plane);

    camera.position.z = 50;
    
    onWindowResize();
   
    animate();
    window.addEventListener("resize", onWindowResize, false);
    function onWindowResize(event) {
        camera.aspect =  window.innerWidth/ window.innerHeight
       camera.updateProjectionMatrix();
       renderer.setSize( window.innerWidth,window.innerHeight);
    }
    
    function animate() {
        requestAnimationFrame(animate);
        
        renderer.render(scene, camera);
    }
</script>

</html>

1.png

这是一个只有四个面的圆柱,接下来让每个面显示不同颜色。

const fshader = ` 
        uniform vec3 color[4];
        uniform float faceCount;
        varying vec2 v_uv;
       
        void main(){
            int index= int(mod(ceil(v_uv.x*faceCount),4.0) );
            gl_FragColor=vec4(color[index],1.0);
         }
        `;
        
const uniforms = {
        color:{value:[
        new THREE.Vector3(1, 0, 0),
        new THREE.Vector3(0, 0, 1),
        new THREE.Vector3(0, 1, 0),
        new THREE.Vector3(1, 1, 0),
        ]},
        faceCount:{value:4}
    }        
     

2.png

color[4] 代表 color 是一个数组 每个元素都为vec3,声明时必须给定长度 对应的uniforms的 color:{value:[...]}这里传入数组即可。

   int 强制类型转换。
   

mod (x,y) x - y * floor(x/y)。

下一节将开始讲解顶点着色器了。