webgl 第三人称相机

2023-11-03

发现自己越来越懒了。。。基础的我不写了,有很多博客都写了,连w3c都有,瞬间没有心思写下去了,并且也很忙;每天都在研究;别骂我。。。真心没心思;我会考虑写一些难度大点的,这样有动力点;

代码如下:
看下面需要注意的是向量的计算,
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>第三人称</title>
    <script src="glMatrix-0.9.6.min.js"></script>
    <script src="../lib/my-webgl-utils.js"></script>
    <script id="vs" type="x-shader/x-vertex">
        attribute vec3 a_Position;
        attribute vec4 a_Color;
        attribute vec2 a_TexCoord;
        uniform mat4 u_Proj;
        varying vec4 v_Color;
        varying vec2 v_texCoord;
        void main(){
            gl_Position = u_Proj * vec4(a_Position,1.0);
            v_Color = a_Color;
            v_texCoord = a_TexCoord;
        }


    </script>
    <script id="fs" type="x-shader/x-fragment">
         precision mediump float;
         varying vec4 v_Color;
         varying vec2 v_texCoord;
         uniform sampler2D u_Texture;
          void main(void){
            gl_FragColor = texture2D(u_Texture,v_texCoord);
          }


    </script>
</head>
<body>
<canvas id="webgl" width="500px" height="500px"></canvas>
<script>
    var gl;
    var viewPortW;
    var viewPortH;

    var triangleBuffer = null;

    var textureHandle;//纹理对象
    var textureGround;
    var projectMat = mat4.create();//投影矩阵
    var viewMat = mat4.create();//视图矩阵
    var cameraEye = new Float32Array(3);//相机位置
    var cameraTrag = new Float32Array(3);//观看目标位置
    var cameraLookAt = new Float32Array(3);
    var cameraUp = new Float32Array(3);

    var fbo;
    var textureDynamic;

    var times = (new Date()).valueOf();

    var role = {};//角色对象
    role._position = new Float32Array(3);
    role._target = new Float32Array(3);
    role._speed = 5;//速度

    var varRotFBOX = 0;
    var varRotFBOY = 0;
    var mouseDown = false;
    var rButtonDown = false;
    var lastMouseX = 0;
    var lastMouseY = 0;

    var radius = 40;
    var loadTextureNum = 0;
    onload = function () {
        var canvas = document.getElementById("webgl");
        gl = canvas.getContext("webgl");
        viewPortW = canvas.clientWidth;
        viewPortH = canvas.clientHeight;
        gl.viewport(0, 0, canvas.clientWidth, viewPortH);

        if (!initShaders(gl, "vs", "fs")) {
            console.log('Failed to intialize shaders.');
            return;
        }
        initLocation();//初始化,获取着色器中的变量地址
        initCamera();
        initModelData();
        canvas.onmousedown = handleMouseDown;
        canvas.onmouseup = handleMouseUp;
        canvas.onmousemove = handleMouseMove;
        canvas.onmousewheel = handleMouseWheel;
        document.onkeydown = handleKeyDown;
        document.onkeyup = handleKeyup;

        tick();
    }

    function tick() {
        requestAnimFrame(tick);
            renderScene();

    }

    function renderScene() {
        renderToFBO();

        var elspsed = (new Date()).valueOf() - times;
        times = (new Date()).valueOf();

        updateTarget(elspsed);
        cameraTrag = role._position;
        updateCamera();//更新相机位置

        varRotFBOX += 1;
        //使用fbo
        gl.bindFramebuffer(gl.FRAMEBUFFER, null);
        gl.viewport(0, 0, viewPortW, viewPortH);

        //! 设置重绘背景的颜色
        gl.clearColor(0.0, 0.0, 0.0, 1.0);
        //! 执行绘制,即将背景清空成制定的颜色(clearColor)
        gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
        gl.enable(gl.DEPTH_TEST);
        //! 指定绘制所使用的顶点数据 从 该缓冲区中获取
        gl.bindBuffer(gl.ARRAY_BUFFER, triangleBuffer);

        var mvp = mat4.create();
        var matTrans = mat4.create();
        var matModel = mat4.create();
        var matRotX = mat4.create();
        var matROtY = mat4.create();
        var matRot = mat4.create();
        var matTemp = mat4.create();

        mat4.identity(matRot);
        mat4.identity(matRotX);
        mat4.identity(matROtY);
        mat4.identity(matTrans);
        mat4.identity(matModel);
        mat4.identity(mvp);
        mat4.identity(matTemp);

        gl.activeTexture(gl.TEXTURE0);
        gl.bindTexture(gl.TEXTURE_2D, textureDynamic);
        gl.uniform1i(gl.uniformTexture, 0);

        mat4.translate(matTrans, [role._position[0], 0.0, role._position[2]]);
        //varRot += 1;

        mat4.rotate(matROtY, degToRad(varRotFBOY), [0.0, 1.0, 0.0]);

        mat4.rotate(matRotX, degToRad(varRotFBOX), [1.0, 0.0, 0.0]);

        mat4.multiply(matROtY, matRotX, matRot);

        mat4.multiply(matTrans, matRot, matModel);

        mat4.multiply(projectMat, viewMat, matTemp);

        mat4.multiply(matTemp, matTrans, mvp);

        gl.uniformMatrix4fv(gl.uniformProj, false, mvp);

        gl.enableVertexAttribArray(gl.a_Position);
        gl.enableVertexAttribArray(gl.a_TexCoord);
        gl.enableVertexAttribArray(gl.a_Color);

        gl.vertexAttribPointer(gl.a_Position, 3, gl.FLOAT, false, 4 * 9, 0);
        gl.vertexAttribPointer(gl.a_TexCoord, 2, gl.FLOAT, false, 4 * 9, 4 * 3);
        gl.vertexAttribPointer(gl.a_Color, 4, gl.FLOAT, false, 4 * 9, 4 * 5);

        gl.drawArrays(gl.TRIANGLES, 0, 36);

        mat4.multiply(projectMat, viewMat, mvp);

        gl.uniformMatrix4fv(gl.uniformProj, false, mvp);
        gl.bindTexture(gl.TEXTURE_2D, textureGround);
        gl.drawArrays(gl.TRIANGLES, 36, 6);
    }

    //计算相机位置
    function updateCamera() {
        //反向推导,知道目标位置,和相机与目标位置的距离,求相机位置
        //camerD 是 目标位置减去相机位置的向量,并归一化,没归一化前,其实就相当于camerD * radius
        //camerD归一化,类似于单位向量,乘以 长,就是它们相减得到的向量
        cameraEye[0] = cameraTrag[0] - cameraLookAt[0] * radius;
        cameraEye[1] = cameraTrag[1] - cameraLookAt[1] * radius;
        cameraEye[2] = cameraTrag[2] - cameraLookAt[2] * radius;
        //规格化up坐标
        var upDir = vec3.normalize(cameraUp);

        mat4.lookAt(cameraEye, cameraTrag, upDir, viewMat);

    }

    //更新目标
    function updateTarget(elapsed) {
        if (role._target[0] == role._position[0]
            && role._target[1] == role._position[1]
            && role._target[2] == role._position[2]) {
            return;
        }

        var offset = new Float32Array(3);
        var dir = new Float32Array(3);
        offset[0] = role._target[0] - role._position[0];
        offset[1] = role._target[1] - role._position[1];
        offset[2] = role._target[2] - role._position[2];

        vec3.normalize(offset, dir);
        var dist = vec3.length(offset);
        if (dist > role._speed * elapsed / 1000.0 * 2) {
            var dist = role._speed * elapsed / 1000.0;
            role._position[0] += dir[0] * dist;
            role._position[2] += dir[2] * dist;
        } else {
            role._position[0] = role._target[0];
            role._position[1] = role._target[1];
            role._position[2] = role._target[2];
        }
    }

    function renderToFBO() {
        //使用fbo
        gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
        gl.viewport(0, 0, viewPortW, viewPortH);

        //设置重绘背景的颜色
        gl.clearColor(1.0, 1.0, 1.0, 1.0);
        //执行绘制,即将背景清空成制定的颜色
        gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
        gl.enable(gl.DEPTH_TEST);
        //指定绘制使用的顶点数据
        gl.bindBuffer(gl.ARRAY_BUFFER, triangleBuffer);

        var mvp = mat4.create();//MVP矩阵
        var matTrans = mat4.create();//移动矩阵
        var matModel = mat4.create();//模型矩阵
        var matRotX = mat4.create();//X旋转矩阵
        var matRotY = mat4.create();//Y 旋转矩阵
        var matRot = mat4.create();//旋转矩阵

        //初始化成单位向量
        mat4.identity(matRot);
        mat4.identity(matRotX);
        mat4.identity(matRotY);
        mat4.identity(matTrans);
        mat4.identity(matModel);
        mat4.identity(mvp);

        gl.activeTexture(gl.TEXTURE0);//开启纹理
        gl.bindTexture(gl.TEXTURE_2D, textureHandle);
        gl.uniform1i(gl.uniformTexture, 0);

        mat4.translate(matTrans, [0, 0.0, -4]);
        mat4.rotate(matRotY, degToRad(varRotFBOY), [0.0, 1.0, 0.0]);
        mat4.rotate(matRotX, degToRad(varRotFBOX), [1.0, 0.0, 0.0]);
        mat4.multiply(matRotY, matRotX, matRot);
        mat4.multiply(matTrans, matRot, matModel);
        mat4.multiply(projectMat, matModel, mvp);//得到mvp 矩阵

        gl.uniformMatrix4fv(gl.uniformProj, false, mvp);

        gl.enableVertexAttribArray(gl.a_Position);
        gl.enableVertexAttribArray(gl.a_Color);
        gl.enableVertexAttribArray(gl.a_TexCoord);

        gl.vertexAttribPointer(gl.a_Position, 3, gl.FLOAT, false, 4 * 9, 0);
        gl.vertexAttribPointer(gl.a_TexCoord, 2, gl.FLOAT, false, 4 * 9, 4 * 3);
        gl.vertexAttribPointer(gl.a_Color, 4, gl.FLOAT, false, 4 * 9, 4 * 5);

        gl.drawArrays(gl.TRIANGLES, 0, 36);
    }

    function degToRad(degrees) {
        return degrees * Math.PI / 180;
    }

    function handleKeyup(event) {

    }

    function handleKeyDown(event) {

    }

    /**
     * 将屏幕坐标转化成世界坐标
     * @param screen
     * @returns {*}
     */
    function screenToWorld(screen) {
        var v = new Float32Array(4);
        var world = new Float32Array(4);

        v[0] = screen[0];
        v[1] = screen[1];
        v[2] = screen[2];
        v[3] = 1.0;//w分量

        //转换成 0 - 1 的坐标
        v[0] = (v[0])/viewPortW;
        v[1] = (viewPortH - v[1])/viewPortH;

        //转换成 -1 到 1 之间的坐标
        v[0] = v[0] * 2.0 - 1.0;
        v[1] = v[1] * 2.0 - 1.0;
        v[2] = v[2] * 2.0 - 1.0;

        var mvp = mat4.create();
        var mvpInvert = mat4.create();
        //计算MVP矩阵
        mat4.multiply(projectMat,viewMat,mvp);
        //求mvp的逆矩阵
        inverseEx(mvp,mvpInvert);
        world[0] = v[0];
        world[1] = v[1];
        world[2] = v[2];
        world[3] = v[3];

        multiply(mvpInvert,v,world);//得到世界坐标

        if (world[3] == 0.0)
        {
            return world;
        }
        world[0]    /=  world[3];
        world[1]    /=  world[3];
        world[2]    /=  world[3];

        return  world;
    }
    function multiply(mat,v,outv) {
        outv[0] =   mat[0] * v[0] + mat[1] * v[1] + mat[2] * v[2] + mat[3] * v[3];
        outv[1] = mat[4] * v[0] + mat[5] * v[1] + mat[6] * v[2] + mat[7] * v[3];
        outv[2] = mat[8] * v[0] + mat[9] * v[1] + mat[10] * v[2] + mat[11] * v[3];
        outv[3] = mat[12] * v[0] + mat[13] * v[1] + mat[14] * v[2] + mat[15] * v[3];
    }
    //逆矩阵
    function inverseEx(pData,resData) {
        var subFactor00 = pData[10] * pData[15] - pData[14] * pData[11];
        var subFactor01 = pData[9] * pData[15] - pData[13] * pData[11];
        var subFactor02 = pData[9] * pData[14] - pData[13] * pData[10];
        var subFactor03 = pData[8] * pData[15] - pData[12] * pData[11];
        var subFactor04 = pData[8] * pData[14] - pData[12] * pData[10];
        var subFactor05 = pData[8] * pData[13] - pData[12] * pData[9];
        var subFactor06 = pData[6] * pData[15] - pData[14] * pData[7];
        var subFactor07 = pData[5] * pData[15] - pData[13] * pData[7];
        var subFactor08 = pData[5] * pData[14] - pData[13] * pData[6];
        var subFactor09 = pData[4] * pData[15] - pData[12] * pData[7];
        var subFactor10 = pData[4] * pData[14] - pData[12] * pData[6];
        var subFactor11 = pData[5] * pData[15] - pData[13] * pData[7];
        var SubFactor12 = pData[4] * pData[13] - pData[12] * pData[5];
        var subFactor13 = pData[6] * pData[11] - pData[10] * pData[7];
        var subFactor14 = pData[5] * pData[11] - pData[9] * pData[7];
        var subFactor15 = pData[5] * pData[10] - pData[9] * pData[6];
        var subFactor16 = pData[4] * pData[11] - pData[8] * pData[7];
        var subFactor17 = pData[4] * pData[10] - pData[8] * pData[6];
        var subFactor18 = pData[4] * pData[9] - pData[8] * pData[5];

        resData[0]  = + pData[5] * subFactor00 - pData[6] * subFactor01 + pData[7] * subFactor02;
        resData[1]  = - pData[4] * subFactor00 + pData[6] * subFactor03 - pData[7] * subFactor04;
        resData[2]  = + pData[4] * subFactor01 - pData[5] * subFactor03 + pData[7] * subFactor05;
        resData[3]  = - pData[4] * subFactor02 + pData[5] * subFactor04 - pData[6] * subFactor05;

        resData[4]  = - pData[1] * subFactor00 + pData[2] * subFactor01 - pData[3] * subFactor02;
        resData[5]  = + pData[0] * subFactor00 - pData[2] * subFactor03 + pData[3] * subFactor04;
        resData[6]  = - pData[0] * subFactor01 + pData[1] * subFactor03 - pData[3] * subFactor05;
        resData[7]  = + pData[0] * subFactor02 - pData[1] * subFactor04 + pData[2] * subFactor05;

        resData[8]  = + pData[1] * subFactor06 - pData[2] * subFactor07 + pData[3] * subFactor08;
        resData[9]  = - pData[0] * subFactor06 + pData[2] * subFactor09 - pData[3] * subFactor10;
        resData[10]  = + pData[0] * subFactor11 - pData[1] * subFactor09 + pData[3] * SubFactor12;
        resData[11]  = - pData[0] * subFactor08 + pData[1] * subFactor10 - pData[2] * SubFactor12;

        resData[12]  = - pData[1] * subFactor13 + pData[2] * subFactor14 - pData[3] * subFactor15;
        resData[13]  = + pData[0] * subFactor13 - pData[2] * subFactor16 + pData[3] * subFactor17;
        resData[14]  = - pData[0] * subFactor14 + pData[1] * subFactor16 - pData[3] * subFactor18;
        resData[15]  = + pData[0] * subFactor15 - pData[1] * subFactor17 + pData[2] * subFactor18;

        var determinant =
            + pData[0] * resData[0]
            + pData[1] * resData[4]
            + pData[2] * resData[8]
            + pData[3] * resData[12];

        for( var i = 0 ;i < 16 ; ++ i)
            resData[i]  /=  determinant;
    }
    function handleMouseDown(event) {

        if (event.button == 0){
            //计算射线
            var minWorld = new Float32Array(3);
            var maxWorld = new Float32Array(3);

            var screen = new Float32Array(3);
            screen[0] = event.offsetX;
            screen[1] = event.offsetY;
            screen[2] = 0.0;

            var screen1 = new Float32Array(3);
            screen[0] = event.offsetX;
            screen[1] = event.offsetY;
            screen[2] = 1.0;

            var minWorld = screenToWorld(screen);
            var maxWorld = screenToWorld(screen1);

            //射线向量
            var dir = new Float32Array(3);
            dir[0] = maxWorld[0] - minWorld[0];
            dir[1] = maxWorld[1] - minWorld[1];
            dir[2] = maxWorld[2] - minWorld[2];
            vec3.normalize(dir);

            //计算交点:
            //计算方式:因为地面的y=0;射线的起点y轴减去地面的y轴得到高,然后用射线的单位向量除以高,minWorld[1]/dir[1],得到速度(步长,)
            //射线的起点位置 加上 tm(步长,相当于时间,走了多长,)乘以 射线的单位向量,得到交点
            //计算时间
            var tm = Math.abs(minWorld[1]/dir[1]);
            var target = new Float32Array(3);
            target[0] = minWorld[0] + tm * dir[0];
            target[1] = minWorld[1] + tm * dir[1];
            target[2] = minWorld[2] + tm * dir[2];

            moveTo(target);
        }else {
            rButtonDown = true;
            lastMouseX = event.offsetX;
            console.log("rButtonDown = true;");
        }
    }
    function moveTo(targetPos) {
        role._target = targetPos;
        role._target[1] = 0;//没有高度
    }
    function handleMouseUp(event) {

    }

    function handleMouseMove(event) {

    }

    function handleMouseWheel(event) {

    }

    function initModelData() {
        var gSize = 100;//大小
        var gPos = -10;//高度
        var rept = 20;//重复

        var boxVertex = [
            //x     y     z     u    v      r    g    b    a
            -1.0, -1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0,
            1.0, -1.0, 1.0, 1.0, 0.0, 1.0, 1.0, 1.0, 1.0,
            1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,

            -1.0, -1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0,
            1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
            -1.0, 1.0, 1.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0,

            -1.0, -1.0, -1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0,
            -1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0,
            1.0, 1.0, -1.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0,

            -1.0, -1.0, -1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0,
            1.0, 1.0, -1.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0,
            1.0, -1.0, -1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0,

            -1.0, 1.0, -1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0,
            -1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0,
            1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0,

            -1.0, 1.0, -1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0,
            1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0,
            1.0, 1.0, -1.0, 1.0, 1.0, 0.0, 1.0, 0.0, 1.0,

            -1.0, -1.0, -1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0,
            1.0, -1.0, -1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0,
            1.0, -1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0,

            -1.0, -1.0, -1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0,
            1.0, -1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0,
            -1.0, -1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 1.0, 1.0,

            1.0, -1.0, -1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0,
            1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 0.0, 1.0,
            1.0, 1.0, 1.0, 0.0, 1.0, 1.0, 1.0, 0.0, 1.0,

            1.0, -1.0, -1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0,
            1.0, 1.0, 1.0, 0.0, 1.0, 1.0, 1.0, 0.0, 1.0,
            1.0, -1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0,

            -1.0, -1.0, -1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0,
            -1.0, -1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0,
            -1.0, 1.0, 1.0, 1.0, 1.0, 0.0, 1.0, 1.0, 1.0,

            -1.0, -1.0, -1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0,
            -1.0, 1.0, 1.0, 1.0, 1.0, 0.0, 1.0, 1.0, 1.0,
            -1.0, 1.0, -1.0, 0.0, 1.0, 0.0, 1.0, 1.0, 1.0,


            -gSize, gPos, -gSize, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0,
            gSize, gPos, -gSize, rept, 0.0, 1.0, 1.0, 1.0, 1.0,
            gSize, gPos, gSize, rept, rept, 1.0, 1.0, 1.0, 1.0,

            -gSize, gPos, -gSize, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0,
            gSize, gPos, gSize, rept, rept, 1.0, 1.0, 1.0, 1.0,
            -gSize, gPos, gSize, 0.0, rept, 1.0, 1.0, 1.0, 1.0,
        ]
        triangleBuffer = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, triangleBuffer);
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(boxVertex), gl.STATIC_DRAW);

        textureHandle = initTextures("test.gif");
        textureGround = initTextures("1.jpg");

        fbo = createFOB(viewPortW, viewPortH);

    }

    //离屏渲染,创建帧缓冲区
    function createFOB(width, height) {
        var obj = gl.createFramebuffer();
        gl.bindFramebuffer(gl.FRAMEBUFFER, obj);

        //创建深度缓冲区
        var depthObj = gl.createRenderbuffer();
        gl.bindRenderbuffer(gl.RENDERBUFFER, depthObj);
        gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, width, height);

        gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthObj);//把深度缓冲区绑定到帧缓冲区

        textureDynamic = createDynamicTexture(width, height);//动态纹理

        gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, textureDynamic, 0);

        gl.bindRenderbuffer(gl.RENDERBUFFER, null);
        gl.bindFramebuffer(gl.FRAMEBUFFER, null);
        gl.bindTexture(gl.TEXTURE_2D, null);
        return obj;
    }

    function createDynamicTexture(width, height) {
        var texture = gl.createTexture();

        gl.bindTexture(gl.TEXTURE_2D, texture);
        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);

        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);

        return texture;
    }

    function initTextures(imageFile) {
        var texture;
        //创建一个纹理
        texture = gl.createTexture();
        //创建一个图片
        texture.image = new Image();
        //指定图片的路径
        texture.image.src = imageFile;
        texture.image.onload = function () {
            handleLoadedTexture(texture);
        }
        return texture;
    }

    function handleLoadedTexture(texture) {
        gl.bindTexture(gl.TEXTURE_2D, texture);
        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.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);

        gl.bindTexture(gl.TEXTURE_2D, null);
        loadTextureNum++;
    }

    function initCamera() {
        cameraEye[0] = 28.017817;
        cameraEye[1] = 29.867514;
        cameraEye[2] = 29.429590;

        cameraTrag[0] = -0.84969789;
        cameraTrag[1] = 1;
        cameraTrag[2] = 0.56207591;

        cameraUp[0] = 0;
        cameraUp[1] = 1;
        cameraUp[2] = 0;

        role._position[0] = 0;
        role._position[1] = 0;
        role._position[2] = 0;

        role._target[0] = 0;
        role._target[1] = 1;
        role._target[2] = 2;

        calcDir();

        mat4.perspective(45, viewPortW / viewPortH, 0.1, 10000.0, projectMat);//设置投影矩阵

        mat4.lookAt(cameraEye, cameraTrag, cameraUp, viewMat);//设置视图矩阵


    }

    //求的lookAt方向 单位向量
    function calcDir() {
        //相机看向位置 减 去 眼睛位置,得到 一个向量, D坐标
        vec3.subtract(cameraTrag, cameraEye, cameraLookAt);

        var d = new Float32Array(3);
        d[0] = cameraTrag[0] - cameraEye[0];
        d[1] = cameraTrag[1] - cameraEye[1];
        d[2] = cameraTrag[2] - cameraEye[2];

        vec3.normalize(d, cameraLookAt);
    }

    function initLocation() {
        gl.uniformProj = gl.getUniformLocation(gl.program, "u_Proj");
        gl.uniformTexture = gl.getUniformLocation(gl.program, "u_Texture");
        gl.a_Position = gl.getAttribLocation(gl.program, "a_Position");
        gl.a_Color = gl.getAttribLocation(gl.program, "a_Color");
        gl.a_TexCoord = gl.getAttribLocation(gl.program, "a_TexCoord");
        if (!gl.uniformProj && !gl.uniformTexture) {
            console.log("err 001:获取地址变量错误!");
        }
        if (gl.a_Position < 0 || gl.a_TexCoord < 0
            || gl.a_Color < 0) {
            console.log("err 002:获取地址变量错误!");
        }
    }

    window.requestAnimFrame = (function () {
        return window.requestAnimationFrame ||
            window.webkitRequestAnimationFrame ||
            window.mozRequestAnimationFrame ||
            window.oRequestAnimationFrame ||
            window.msRequestAnimationFrame ||
            function (callback, element) {
                window.setTimeout(callback, 10);
            };
    })();
    function inverseEx(pData,resData)
    {
        var subFactor00 = pData[10] * pData[15] - pData[14] * pData[11];
        var subFactor01 = pData[9] * pData[15] - pData[13] * pData[11];
        var subFactor02 = pData[9] * pData[14] - pData[13] * pData[10];
        var subFactor03 = pData[8] * pData[15] - pData[12] * pData[11];
        var subFactor04 = pData[8] * pData[14] - pData[12] * pData[10];
        var subFactor05 = pData[8] * pData[13] - pData[12] * pData[9];
        var subFactor06 = pData[6] * pData[15] - pData[14] * pData[7];
        var subFactor07 = pData[5] * pData[15] - pData[13] * pData[7];
        var subFactor08 = pData[5] * pData[14] - pData[13] * pData[6];
        var subFactor09 = pData[4] * pData[15] - pData[12] * pData[7];
        var subFactor10 = pData[4] * pData[14] - pData[12] * pData[6];
        var subFactor11 = pData[5] * pData[15] - pData[13] * pData[7];
        var SubFactor12 = pData[4] * pData[13] - pData[12] * pData[5];
        var subFactor13 = pData[6] * pData[11] - pData[10] * pData[7];
        var subFactor14 = pData[5] * pData[11] - pData[9] * pData[7];
        var subFactor15 = pData[5] * pData[10] - pData[9] * pData[6];
        var subFactor16 = pData[4] * pData[11] - pData[8] * pData[7];
        var subFactor17 = pData[4] * pData[10] - pData[8] * pData[6];
        var subFactor18 = pData[4] * pData[9] - pData[8] * pData[5];

        resData[0]  = + pData[5] * subFactor00 - pData[6] * subFactor01 + pData[7] * subFactor02;
        resData[1]  = - pData[4] * subFactor00 + pData[6] * subFactor03 - pData[7] * subFactor04;
        resData[2]  = + pData[4] * subFactor01 - pData[5] * subFactor03 + pData[7] * subFactor05;
        resData[3]  = - pData[4] * subFactor02 + pData[5] * subFactor04 - pData[6] * subFactor05;

        resData[4]  = - pData[1] * subFactor00 + pData[2] * subFactor01 - pData[3] * subFactor02;
        resData[5]  = + pData[0] * subFactor00 - pData[2] * subFactor03 + pData[3] * subFactor04;
        resData[6]  = - pData[0] * subFactor01 + pData[1] * subFactor03 - pData[3] * subFactor05;
        resData[7]  = + pData[0] * subFactor02 - pData[1] * subFactor04 + pData[2] * subFactor05;

        resData[8]  = + pData[1] * subFactor06 - pData[2] * subFactor07 + pData[3] * subFactor08;
        resData[9]  = - pData[0] * subFactor06 + pData[2] * subFactor09 - pData[3] * subFactor10;
        resData[10]  = + pData[0] * subFactor11 - pData[1] * subFactor09 + pData[3] * SubFactor12;
        resData[11]  = - pData[0] * subFactor08 + pData[1] * subFactor10 - pData[2] * SubFactor12;

        resData[12]  = - pData[1] * subFactor13 + pData[2] * subFactor14 - pData[3] * subFactor15;
        resData[13]  = + pData[0] * subFactor13 - pData[2] * subFactor16 + pData[3] * subFactor17;
        resData[14]  = - pData[0] * subFactor14 + pData[1] * subFactor16 - pData[3] * subFactor18;
        resData[15]  = + pData[0] * subFactor15 - pData[1] * subFactor17 + pData[2] * subFactor18;

        var determinant =
            + pData[0] * resData[0]
            + pData[1] * resData[4]
            + pData[2] * resData[8]
            + pData[3] * resData[12];

        for( var i = 0 ;i < 16 ; ++ i)
            resData[i]  /=  determinant;
    }
    function multiply(mat,v,outv) {
        outv[0] =   mat[0] * v[0] + mat[1] * v[1] + mat[2] * v[2] + mat[3] * v[3];
        outv[1] = mat[4] * v[0] + mat[5] * v[1] + mat[6] * v[2] + mat[7] * v[3];
        outv[2] = mat[8] * v[0] + mat[9] * v[1] + mat[10] * v[2] + mat[11] * v[3];
        outv[3] = mat[12] * v[0] + mat[13] * v[1] + mat[14] * v[2] + mat[15] * v[3];
    }
</script>
</body>
</html>
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

webgl 第三人称相机 的相关文章

  • 前端性能优化方案都有哪些?

    前端是庞大的 包括 HTML CSS Javascript Image Flash等等各种各样的资源 前端优化是复杂的 针对方方面面的资源都有不同的方式 那么 前端优化的目的是什么 1 从用户角度而言 优化能够让页面加载得更快 对用户的操作
  • Spring事务的传播行为REQUIRES_NEW和REQUIRED区别

    事务的传播特性指的是当一个事务方法被另一个事务方法调用时 这个事务方法应该如何进行 spring的事务传播行为一共有7种 备注 上面图是从其他博客截取的 忘记链接了 应用场景 A方法中调用B和C方法 其中BC都进行 1操作 括号中的报错意思
  • 华为OD机试真题-光伏场地建设规划【2023Q1】【JAVA、Python、C++】

    题目描述 祖国西北部有一片大片荒地 其中零星的分布着一些湖泊 保护区 矿区 整体上常年光照良好 但是也有一些地区光照不太好 某电力公司希望在这里建设多个光伏电站 生产清洁能源 对每平方公里的土地进行了发电评估 其中不能建设的区域发电量为0k
  • python安装时间过长_python走过的坑(关于python、pip安装)

    第一个坑 python2 or python3 how to choose 变化不大看你目前的研究方向的库支持 python的编辑器选择问题 IDLE Subline text 这属于文本编辑器 pycharm Anaconda IDE集成
  • 【Unity小游戏】游戏开发案例,轻松打造一款塔防游戏!(下)

    欢迎来到如何在 Unity 中创建塔防游戏的第二部分 你正在Unity中制作一个塔防游戏 在第一部分结束时 你可以放置和升级怪物 你还有一个敌人攻击饼干 然而 敌人不知道该面对哪条路 此外 这是攻击的一个严重的失误 在这一部分中 你将添加敌
  • [转载]OpenWRT UCI API的使用

    OpenWRT UCI API的使用 分类 OpenWRT C 2014 03 09 21 51 3551人阅读 评论 4 收藏 举报 目录 原文 http blog csdn net bywayboy article details 20
  • 阿里测试8年,肝到P8只剩他了····

    在阿里工作了8年 工作压力大 节奏快 但是从技术上确实得到了成长 尤其是当你维护与大促相关的系统的时候 熬到P7也费了不少心思 小编也是个爱学习的人 把这几年的工作经验整理成了一份完整的笔记 此笔记已助朋友拿到腾讯 阿里 美团等10个off
  • Python表白代码合集:5种表白代码,找不到对象你来找我,这也太秀了叭

    文章目录 一 容我啰嗦两句 二 来吧 代码展示 1 给女神比个小心心 2 无限弹窗式表白 3 这货不是表白代码 悄悄送给你们 4 520表白墙 5 抖音热门表白小软件 6 无套路表白 三 写在最后 一 容我啰嗦两句 爬虫看多了 对身体不好
  • 在win7中chm打不开的最佳方法

    我今天遇到了个非常棘手的问题 想看看资料 却打不开chm帮助文件 我装的是64位win7 打开chm文件时 系统提示我安全问题 强行打开 却无法显示内容 得到的提示是 该页导航被取消 郁闷啊 头痛啊 怎么办啊 急急如火令 只有找Google
  • uni-app 自定义webview大小

    在uni app中使用web view 组件 默认铺满全屏并且层级高于前端组件 会遮挡页面上的其他组件 在官网中写明 app vue下web view组件不支持自定义样式 默认充满屏幕不可控制大小 nvue web view 必须指定样式宽
  • Unity3D 如何在ECS架构下,用Unity引擎进行游戏开发详解

    前言 Unity3D是一款强大的游戏引擎 它提供了丰富的功能和工具 可以帮助开发者快速构建高质量的游戏 而Entity Component System ECS 是Unity3D中一种新的架构模式 它可以提高游戏的性能和可扩展性 本文将详细
  • go操作excelize报表框架

    Excelize系统设计 养成习惯 再看 源码在文章后面 写在前面 感谢go社区的excelize框架 https github com 360EntSecGroup Skylar excelize 版本说明 这个版本只是第一版 简单利用了
  • 计算0到100中所有含有6的数之和(注意不要生复算66)(用与10取模计算出各位有6的数,用除10 计算十位有6的数)

    import
  • DHCP的概念和原理

    提示 文章写完后 目录可以自动生成 如何生成可参考右边的帮助文档 文章目录 一 什么是DHCP 1 dhcp的作用 2 DHCP工作原理 3 DHCP服务器为客户端分配IP地址有三种形式 4 DHCP的好处 5 DHCP种的租期 6 DHC
  • 服务端授权工具可做跟单和程序化交易源码

    源码介绍 MT4行情抓取工具 用vc 开发 源码全开放 速度飞快 功能稳定 界面显示订阅代码的实时数据 直观 操作简单 可以灵活修改 添加订阅代码 订阅需要的行情数据 可用unifeed协议给MT4服务端做数据源 也可以自己修改数据输出格式
  • 58 openEuler搭建Mariadb数据库服务器-管理数据库

    文章目录 58 openEuler搭建Mariadb数据库服务器 管理数据库 58 1 创建数据库 58 2 查看数据库 58 3 选择数据库 58 4 删除数据库 58 5 备份数据库 58 6 恢复数据库 58 openEuler搭建M
  • BLE 蓝牙的一些心得总结

    1 TI 的CC2541协议栈开发教程 https blog csdn net feilusia category 5630377 html 2 以下是我对BLE 蓝牙的一些理解 如果有什么不对的地方 恳请大佬们指点
  • java源码分析-native方法的调用

    java源码分析 native方法的调用 这段时间在分析java源码时 经常能看到很多的底层源码中都调用了被native关键字修饰的方法 也就是java调用本地方法 但是在进行debug时有进不去 看不了具体的实现 因为这写方法是用c c

随机推荐

  • string字符串查找和替换

    字符串的查找 string find 函数 这个函数比较容易理解 就是按照string 的正顺序往后进行对比 查找str第一次出现的位置 如果可以找到 则返回在sring的位置 不能找到的话 返回 1 因此我们可以写一个if函数进行判断 v
  • VsCode中好用的git源代码管理插件GitLens

    git多人协作的时候需要查看日志 如果能在当前代码中查看到那是很方便的一件事 能省去很多时间去其他工具查看 提高工作效率 VsCode中的GitLens插件刚好能满足这个需求 而且搭配Cmder使用 很舒服 不会出现换行不工整的问题 还可以
  • QImage 类的基本操作

    一 显示方法 两种 第一种更佳 1 QImage转QPixmap 然后用QLabel setPixmap 1 2 3 4 5 6 7 image new QImage D Temp XX jpg pixmap newQPixmap pixm
  • Python识别PDF扫描版PDF纯图PDF,OCR提取汉字的10大方法,力推RapidOCRPDF 可识别纯图PDF 加密签名的PDF 重点是开源免费,某些方面准确度比百度OCR高

    下面实例都以下面的测试样例PDF为实验对象 非纯图可复制pdf 纯图PDF TOP1 RapidOCRPDF 可识别纯图PDF也能识别加密签名的PDF 重点是开源免费 https github com RapidAI RapidOCRPDF
  • Pytorch多进程Queue通信产生Segmentation fault (core dumped)——解决方案及代码规范

    最近在做一个强化学习的项目 运用多进程分布训练时遇到了段错误的问题 这里记录下解决的过程思路和方案 由于智能体与环境交互的过程涉及到了第三方的程序以及大量的文件读写操作 使得整个实验过程非常慢 为了解决交互部分的速度瓶颈 采用Ape X D
  • 深度之眼【Pytorch】-读取自己的数据 Dataset 和 ImageFolder

    本文为深度之眼pytorch训练营二期学习笔记 详细课程内容移步 深度之眼 https ai deepshare net index 目录 重写Dataset类 例子一 通过 包含 数据路径 与 标签 的文件读取 例子二 通过标签文件读取
  • WinPE无法识别NVMe SSD硬盘,如何重装系统

    源自网络出处不详 抽风 diy一台新机器 下载的win10系统安装时出现如题所示的问题 开始以为是主板的问题设置u盘启动也不行 后来在某个群里有人说是系统版本问题 无奈重新做了启动优盘 用的17年的win10系统当时这系统有毛病 但是为了测
  • Android studio 多渠道开发App以及打包方法

    Android studio 多渠道开发App及打包方法 大家好 技术小白又来总结经验了 开发过程中我们的App首先需要在开发环境下开发 开发完之后需要打包到测试环境提供给测试人员开发 测试人员测试通过后需要打包到正式环境进行上线 这些过程
  • JVM类加载图示

    拿图请标明出处 内容来源 深入理解Java虚拟机
  • 代码随想录算法训练营第一天

    代码随想录算法训练营第一天 704 二分查找 27 移除元素 数组理论基础 二维数组 704 二分查找 左闭右闭写法 左闭右开写法 注意点 总结 27 移除元素 暴力解法 双指针思路 复杂度 感想 数组理论基础 数组主要考察对代码的掌握能力
  • office修复找不到msi_Microsoft Office安装程序找不到ProPlus.WW\ProPlusWW.msi 弄不了

    展开全部 解决办法就是重新下载一个完整的安装62616964757a686964616fe4b893e5b19e31333366306538包 也可以在网上单独下载proplusww msi Microsoft Office安装程序找不到P
  • Python程序员面试必备常用问题答案及解析

    1 什么是Python Python是一种编程语言 它有对象 模块 线程 异常处理和自动内存管理 可以加入与其他语言的对比 下面是回答这一问题的几个关键点 a Python是一种解释型语言 python代码在运行之前不需要编译 b Pyth
  • DC-2靶机渗透

    首先获取目标主机ip netdiscover i eth0 r 192 168 88 1 24 对端口进行扫描 只开放了web服务 于是访问web 这里配置本地dns解析 将目标ip地址添加进hosts 成功访问 发现是wordpress
  • Springboot项目打包war配置详解

    Springboot项目打包war配置详解 1 排除内置tomcat依赖 2 添加servlet依赖 3 修改打包方式 4 修改主启动类 5 完整pom xml 6 效果图 1 排除内置tomcat依赖
  • C++多态

    文章目录 多态 重写 虚函数 C 11 override 和 final 重载 重写 重定义 抽象类 接口继承和实现继承 多态的原理 虚函数表 单继承和多继承的虚函数表 总结 多态 多态 可以理解为一种事务有多种形态 不同的对象可以通过多态
  • 牛客错题集(2)

    这里写目录标题 专业知识 计算机组成原理 数据结构 C C 操作系统 计算机网络 数据库 软件测试 软件工程 知识盲区 运维 JAVA 编程基础 Linux 网络基础 编译和体系结构 前端 专业知识 计算机组成原理 Q 由于CPU内部的操作
  • cout执行顺序从右到左!!!

    cout在执行的时候为从右向左先执行 输出时为按照原来的顺序在从左向右输出
  • Animate cc怎么导出HTML,使用 Animate CC 导出图形和视频

    导出应用程序首选参数 您可以在专家和初学者首选参数之间切换 您还可将自己的应用程序设置 包括工具栏设置 键盘快捷键和其他应用程序设置 导出为单个文件 只需进行简单的文件导出和导入操作 即可跨设备应用您的应用程序首选参数 导出首选参数 导出全
  • ipfs-hdfs分布式文件系统

    HDFS hadoop项目的核心子项目 基于流数据模式访问和处理超大文件的需求而开发的 数据的分布式存储和处理 namenode 和 datanode 的内置服务器可帮助用户轻松检查群集的状态 namenode 管理维护着文件系统树以及整个
  • webgl 第三人称相机

    发现自己越来越懒了 基础的我不写了 有很多博客都写了 连w3c都有 瞬间没有心思写下去了 并且也很忙 每天都在研究 别骂我 真心没心思 我会考虑写一些难度大点的 这样有动力点 代码如下 看下面需要注意的是向量的计算