11、平行光

创建日期:2024-07-08
更新日期:2025-01-12
<!DOCTYPE html>

<html lang="zh-CN">

<head>
    <meta charset="UTF-8" />
    <title>11_平行光</title>
    <script src="../js/three.js"></script>

    <!-- 顶点着色器 -->
    <script id="shader-vs" type="x-shader/x-vertex">
        attribute vec3 aVertexPosition;
        attribute vec3 aVertexNormal;
        attribute vec2 aTextureCoord;

        uniform mat4 uMVMatrix;
        uniform mat4 uNMatrix;

        uniform vec3 uLightingDirection;
        uniform vec3 uDirectionalColor;

        varying vec2 vTextureCoord;
        varying vec3 vLightWeighting;

        void main(void) {
        gl_Position = uMVMatrix * vec4(aVertexPosition, 1.0);
        vTextureCoord = aTextureCoord;
        vec3 transformedNormal = (uNMatrix * vec4(aVertexNormal, 1.0)).xyz;
        float directionalLightWeighting = max(dot(transformedNormal, uLightingDirection), 0.0);
        vLightWeighting = uDirectionalColor * directionalLightWeighting;
        }
    </script>

    <!-- 片源着色器 -->
    <script id="shader-fs" type="x-shader/x-fragment">
        precision mediump float;
        
        uniform sampler2D uSampler;

        varying vec2 vTextureCoord;
        varying vec3 vLightWeighting;

        void main(void) {
        vec4 textureColor = texture2D(uSampler, vec2(vTextureCoord.s, vTextureCoord.t));
        gl_FragColor = vec4(textureColor.rgb * vLightWeighting, textureColor.a);
        }
    </script>

    <script type="text/javascript">
        var start = function () {
            // 上下文环境
            var canvas = document.getElementById("mycanvas");
            var gl = canvas.getContext("experimental-webgl");
            gl.viewportWidth = canvas.width;
            gl.viewportHeight = canvas.height;
            gl.viewport(0, 0, gl.viewportWidth, gl.viewportWidth);
            gl.enable(gl.DEPTH_TEST);

            // 顶点着色器
            var shader = gl.createShader(gl.VERTEX_SHADER);
            gl.shaderSource(shader, document.getElementById('shader-vs').textContent);
            gl.compileShader(shader);
            var vertexShader = shader;

            // 片源着色器
            var shader = gl.createShader(gl.FRAGMENT_SHADER);
            gl.shaderSource(shader, document.getElementById('shader-fs').textContent);
            gl.compileShader(shader);
            var fragmentShader = shader;

            // 着色器程序
            var shaderProgram = gl.createProgram();
            gl.attachShader(shaderProgram, vertexShader);
            gl.attachShader(shaderProgram, fragmentShader);
            gl.linkProgram(shaderProgram);
            gl.useProgram(shaderProgram);

            // 获取参数地址
            var vertexPositionAttr = gl.getAttribLocation(shaderProgram, 'aVertexPosition');
            gl.enableVertexAttribArray(vertexPositionAttr);
            var vertexNormalAttr = gl.getAttribLocation(shaderProgram, "aVertexNormal");
            gl.enableVertexAttribArray(vertexNormalAttr);
            var textureCoordAttr = gl.getAttribLocation(shaderProgram, 'aTextureCoord');
            gl.enableVertexAttribArray(textureCoordAttr);
            var nMatrixUniform = gl.getUniformLocation(shaderProgram, "uNMatrix");
            var mvMatrixUniform = gl.getUniformLocation(shaderProgram, 'uMVMatrix');
            var lightingDirectionUniform = gl.getUniformLocation(shaderProgram, "uLightingDirection");
            var directionalColorUniform = gl.getUniformLocation(shaderProgram, "uDirectionalColor");
            var samplerUniform = gl.getUniformLocation(shaderProgram, 'uSampler');

            // 顶点位置
            var vertices = [
                // 前面
                 -0.5, -0.5, 0.5,
                 0.5, -0.5, 0.5,
                 0.5, 0.5, 0.5,
                 -0.5, 0.5, 0.5,

                 // 后面
                 -0.5, -0.5, -0.5,
                 0.5, -0.5, -0.5,
                 0.5, 0.5, -0.5,
                 -0.5, 0.5, -0.5,

                 // 左面
                 -0.5, -0.5, -0.5,
                 -0.5, -0.5, 0.5,
                 -0.5, 0.5, 0.5,
                 -0.5, 0.5, -0.5,

                 // 右面
                 0.5, -0.5, 0.5,
                 0.5, -0.5, -0.5,
                 0.5, 0.5, -0.5,
                 0.5, 0.5, 0.5,

                 // 上面
                 -0.5, 0.5, 0.5,
                 0.5, 0.5, 0.5,
                 0.5, 0.5, -0.5,
                 -0.5, 0.5, -0.5,

                 // 下面
                 -0.5, -0.5, 0.5,
                 0.5, -0.5, 0.5,
                 0.5, -0.5, -0.5,
                 -0.5, -0.5, -0.5,
            ];
            var vertexPositionBuffer = gl.createBuffer();
            gl.bindBuffer(gl.ARRAY_BUFFER, vertexPositionBuffer);
            gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);

            // 顶点索引
            var vertexIndices = [
                // 前面
                0, 1, 2,
                0, 2, 3,

                // 后面
                4, 5, 6,
                4, 6, 7,

                // 左面
                8, 9, 10,
                8, 10, 11,

                // 右面
                12, 13, 14,
                12, 14, 15,

                // 上面
                16, 17, 18,
                16, 18, 19,

                // 下面
                20, 21, 22,
                20, 22, 23
            ];
            var vertexIndexBuffer = gl.createBuffer();
            gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, vertexIndexBuffer);
            gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(vertexIndices), gl.STATIC_DRAW);

            // 顶点法线
            var vertexNormals = [
                // 前面
                 0.0, 0.0, 1.0,
                 0.0, 0.0, 1.0,
                 0.0, 0.0, 1.0,
                 0.0, 0.0, 1.0,

                // 后面
                 0.0, 0.0, -1.0,
                 0.0, 0.0, -1.0,
                 0.0, 0.0, -1.0,
                 0.0, 0.0, -1.0,

                 // 左面
                -1.0, 0.0, 0.0,
                -1.0, 0.0, 0.0,
                -1.0, 0.0, 0.0,
                -1.0, 0.0, 0.0,

                // 右面
                 1.0, 0.0, 0.0,
                 1.0, 0.0, 0.0,
                 1.0, 0.0, 0.0,
                 1.0, 0.0, 0.0,

                // 上面
                 0.0, 1.0, 0.0,
                 0.0, 1.0, 0.0,
                 0.0, 1.0, 0.0,
                 0.0, 1.0, 0.0,

                // 下面
                 0.0, -1.0, 0.0,
                 0.0, -1.0, 0.0,
                 0.0, -1.0, 0.0,
                 0.0, -1.0, 0.0
            ];
            var cubeVertexNormalBuffer = gl.createBuffer();
            gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexNormalBuffer);
            gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertexNormals), gl.STATIC_DRAW);

            // 纹理坐标
            var textureCoords = [
                // 前面
                0.0, 0.0,
                1.0, 0.0,
                1.0, 1.0,
                0.0, 1.0,

                // 后面
                0.0, 0.0,
                1.0, 0.0,
                1.0, 1.0,
                0.0, 1.0,

                // 左面
                0.0, 0.0,
                1.0, 0.0,
                1.0, 1.0,
                0.0, 1.0,

                // 右面
                0.0, 0.0,
                1.0, 0.0,
                1.0, 1.0,
                0.0, 1.0,

                // 上面
                0.0, 0.0,
                1.0, 0.0,
                1.0, 1.0,
                0.0, 1.0,

                // 下面
                0.0, 0.0,
                1.0, 0.0,
                1.0, 1.0,
                0.0, 1.0
            ];
            var vertexTextureCoordBuffer = gl.createBuffer();
            gl.bindBuffer(gl.ARRAY_BUFFER, vertexTextureCoordBuffer);
            gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(textureCoords), gl.STATIC_DRAW);

            // 纹理
            var texture = gl.createTexture();
            texture.image = new Image();
            texture.image.onload = function () {
                gl.bindTexture(gl.TEXTURE_2D, texture);
                gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
                gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, texture.image);

                gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
                gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);

                gl.bindTexture(gl.TEXTURE_2D, null);
            }
            texture.image.src = "../image/crate.gif";

            // 渲染循环
            var x = 0, y = 0, z = 0;
            var rotateSpeed = 0.01; // 旋转速度
            var scale = 1.0; // 缩放级别
            var render = function () {
                // 清屏
                gl.clearColor(0.0, 0.0, 0.0, 1.0);
                gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

                // 顶点
                gl.bindBuffer(gl.ARRAY_BUFFER, vertexPositionBuffer);
                gl.vertexAttribPointer(vertexPositionAttr, 3, gl.FLOAT, false, 0, 0);

                // 顶点法线
                gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexNormalBuffer);
                gl.vertexAttribPointer(vertexNormalAttr, 3, gl.FLOAT, false, 0, 0);

                // 纹理坐标
                gl.bindBuffer(gl.ARRAY_BUFFER, vertexTextureCoordBuffer);
                gl.vertexAttribPointer(textureCoordAttr, 2, gl.FLOAT, false, 0, 0);

                // 纹理
                gl.activeTexture(gl.TEXTURE0);
                gl.bindTexture(gl.TEXTURE_2D, texture);
                gl.uniform1i(samplerUniform, 0);

                // 平行光
                var lightMat = new THREE.Vector3(1.0, 1.0, 1.0);
                lightMat.normalize();
                lightMat.multiplyScalar(-1);
                gl.uniform3fv(lightingDirectionUniform, lightMat.toArray()); // 方向
                gl.uniform3f(directionalColorUniform, 1.0, 1.0, 1.0); // 颜色

                // 位移矩阵
                var mvEuler = new THREE.Euler(x, y, z);
                var mvMatrix = new THREE.Matrix4();
                mvMatrix.makeRotationFromEuler(mvEuler);

                // 缩放矩阵
                var scaleMatrix = new THREE.Matrix4();
                scaleMatrix.makeScale(scale, scale, scale);
                gl.uniformMatrix4fv(mvMatrixUniform, false, scaleMatrix.multiply(mvMatrix).toArray());

                // 位移矩阵法线
                var normalMatrix = new THREE.Matrix4();
                normalMatrix.getInverse(mvMatrix);
                normalMatrix.transpose();
                gl.uniformMatrix4fv(nMatrixUniform, false, normalMatrix.toArray());

                // 绘制
                gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, vertexIndexBuffer);
                gl.drawElements(gl.TRIANGLES, 36, gl.UNSIGNED_SHORT, 0);

                x += rotateSpeed;
                y += rotateSpeed;
                z += rotateSpeed;
                requestAnimationFrame(render);
            }

            // 键盘输入
            document.onkeypress = function (event) {
                var key = String.fromCharCode(event.keyCode).toLowerCase();
                if (key == 'a') {
                    rotateSpeed += 0.01;
                } else if (key == 's') {
                    rotateSpeed -= 0.01
                } else if (key == 'd') {
                    scale += 0.1;
                } else if (key == 'f') {
                    scale -= 0.1;
                }
                if (rotateSpeed <= 0.0) {
                    rotateSpeed = 0.0;
                }
                if (scale <= 0.0) {
                    scale = 0.0;
                }
                if (scale >= 2.0) {
                    scale = 2.0;
                }
            }

            render();
        }
    </script>
</head>

<body onload="start();">
    <canvas id="mycanvas" style="border: none;" width="500" height="500"></canvas>
</body>

</html>