2、顶点着色器之视图矩阵

1、作用:将物体从世界坐标系转换到相机坐标系,相当于从世界坐标系转换到相机的局部(本地)坐标系。

2、基于LookAt函数的视图矩阵:

相机位置eye:(ex,ey,ez),世界坐标系下的位置

目标位置center:(cx,cy,cz),这是相机朝向的点,也是世界坐标系下的位置

上方向up:(ux,uy,uz),用于定义相机的上方向,一般都是世界坐标系的上方向,这样相机才是正对着物体而不是倾斜的

构建视图矩阵的步骤如下:

  1. 计算相机的方向向量(z轴方向,camera direction,相机坐标系的“视线方向”在世界坐标系中的表示):从相机位置到目标位置的反方向,用于定义新的Z轴
    z = e y e − c e n t e r ∣ e y e − c e n t e r ∣ \mathbf{z}=\frac{\mathbf{eye}-\mathbf{center}}{|\mathbf{eye}-\mathbf{center}|} z=eyecentereyecenter
  2. 计算相机的右方向向量(x轴方向,camera right,相机坐标系的“右方向”在世界坐标系中的表示):通过向上方向和摄像机方向的叉积,计算出相机的右方向,用于定义新的x轴
    x = u p × z ∣ u p × z ∣ \mathbf{x}=\frac{\mathbf{up}×\mathbf{z}}{|\mathbf{up}×\mathbf{z}|} x=up×zup×z
  3. 计算相机的上方向向量(y轴方向,camera up,相机坐标系的“上方向”在世界坐标系中的表示):通过相机的Z轴和X轴的叉积,得到新的Y轴方向
    y = z × x \mathbf{y}=\mathbf{z}×\mathbf{x} y=z×x
  4. 构建视图矩阵:根据上面计算的向量,视图矩阵可以写成如下形式:
    V i e w M a t r i x = [ x x x y x z − x ⋅ e y e y x y y y z − y ⋅ e y e z x z y z z − z ⋅ e y e 0 0 0 1 ] \mathbf{ViewMatrix}=\begin{bmatrix} x_x & x_y & x_z & −\mathbf{x⋅eye}\\ y_x & y_y & y_z & −\mathbf{y⋅eye}\\ z_x & z_y & z_z & −\mathbf{z⋅eye}\\ 0&0&0&1\end{bmatrix} ViewMatrix= xxyxzx0xyyyzy0xzyzzz0xeyeyeyezeye1
    3、示例代码:
// matrix.js
const regPos = /^-?\d+(\.\d+)?$/; // 支持整数和浮点数,支持负号
function isVector3D(vector) {
  if (!Array.isArray(vector)) return false;
  if (vector.length != 3) return false;
  return (
    regPos.test(vector[0]) && regPos.test(vector[1]) && regPos.test(vector[2])
  );
}

function normalized(vector) {
  if (!isVector3D(vector)) return null;

  const vectorLength = Math.sqrt(
    Math.pow(vector[0], 2) + Math.pow(vector[1], 2) + Math.pow(vector[2], 2)
  );
  return vector.map((item) => {
    return item / vectorLength;
  });
}

function cross(v1, v2) {
  if (!isVector3D(v1)) return null;
  if (!isVector3D(v2)) return null;
  return [
    v1[1] * v2[2] - v1[2] * v2[1],
    v1[2] * v2[0] - v1[0] * v2[2],
    v1[0] * v2[1] - v1[1] * v2[0],
  ];
}

function dot(v1, v2) {
  if (!isVector3D(v1)) return null;
  if (!isVector3D(v2)) return null;
  return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2];
}

function lookAt(eye, target, up = [0, 1, 0]) {
  if (!isVector3D(eye)) return null;
  if (!isVector3D(target)) return null;
  if (!isVector3D(up)) return null;

  const eyeMinusTarget = eye.map((item, index) => item - target[index]);
  const z = normalized(eyeMinusTarget); // Z轴
  const x = normalized(cross(up, z)); // X轴
  const y = cross(z, x); // Y轴

  // glsl中的mat4类型是列主序的,这里也要改为列主序
  return new Float32Array([
    x[0],
    y[0],
    z[0],
    0,
    x[1],
    y[1],
    z[1],
    0,
    x[2],
    y[2],
    z[2],
    0,
    -dot(x, eye),
    -dot(y, eye),
    -dot(z, eye),
    1,
  ]);
}

export { isVector3D, normalized, dot, cross, lookAt };
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>WebGL2 视图矩阵示例</title>
    <style>
        html,
        body {
            margin: 0;
            overflow: hidden;
        }

        canvas {
            position: fixed;
            top: 0;
            left: 0;
            outline: none;
            width: 100%;
            height: 100%;
        }
    </style>
</head>

<body>
    <canvas id="webgl-canvas"></canvas>
    <div style="display: flex;position: fixed;left: 10px;top: 10px;">
        <button id="front">从正面看</button>
        <button id="back">从背面看</button>

    </div>
    <script type="module">
        import { lookAt } from './matrix.js'
        const canvas = document.getElementById("webgl-canvas");
        const gl = canvas.getContext("webgl2");

        if (!gl) {
            console.log("WebGL2 not supported, falling back on WebGL");
        }

        const vertexShaderSource = `#version 300 es
        in vec4 aPosition;
        uniform mat4 uViewMatrix;
        void main() {
            gl_Position = uViewMatrix * aPosition;
        }`;

        const fragmentShaderSource = `#version 300 es
        precision highp float;
        out vec4 outColor;
        void main() {
            outColor = vec4(1.0, 0.0, 0.0, 1.0);  // Red color
        }`;

        function createShader(gl, type, source) {
            const shader = gl.createShader(type);
            gl.shaderSource(shader, source);
            gl.compileShader(shader);
            if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
                console.error("Shader compile failed:", gl.getShaderInfoLog(shader));
                gl.deleteShader(shader);
                return null;
            }
            return shader;
        }

        function createProgram(gl, vertexShader, fragmentShader) {
            const program = gl.createProgram();
            gl.attachShader(program, vertexShader);
            gl.attachShader(program, fragmentShader);
            gl.linkProgram(program);
            if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
                console.error("Program link failed:", gl.getProgramInfoLog(program));
                gl.deleteProgram(program);
                return null;
            }
            return program;
        }

        const vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexShaderSource);
        const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSource);
        const program = createProgram(gl, vertexShader, fragmentShader);

        const positionBuffer = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
        const size = 0.5;
        const positions = new Float32Array([
            -size, -size, size,
            size, -size, size,
            -size, size, size,
            size, -size, size,
            size, size, size,
            -size, size, size,
        ]);
        gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW);

        const vao = gl.createVertexArray();
        gl.bindVertexArray(vao);

        const aPosition = gl.getAttribLocation(program, "aPosition");
        gl.enableVertexAttribArray(aPosition);
        gl.vertexAttribPointer(aPosition, 3, gl.FLOAT, false, 0, 0);

        gl.useProgram(program);

        // 根据设备的像素比率调整 canvas 尺寸,否则很模糊
        const pixelRatio = window.devicePixelRatio || 1;
        canvas.width = canvas.clientWidth * pixelRatio;
        canvas.height = canvas.clientHeight * pixelRatio;
        gl.viewport(0, 0, canvas.width, canvas.height);

        // 设置视图矩阵
        const uViewMatrix = gl.getUniformLocation(program, "uViewMatrix");
        let viewMatrix = new Float32Array([
            1, 0, 0, 0,
            0, 1, 0, 0,
            0, 0, 1, 0,
            0, 0, 0, 1
        ])
        gl.uniformMatrix4fv(uViewMatrix, false, viewMatrix);

        document.getElementById("back").onclick = (e) => {
            // 相机在红色矩形的后面,由于启用了背面剔除,所以看不到
            viewMatrix = lookAt([0, 0, -1], [0, 0, 0], [0, 1, 0])
            gl.uniformMatrix4fv(uViewMatrix, false, viewMatrix);
            alert("相机在(0,0,-1)处看向(0,0,0)处,相机在红色矩形的后面,由于启用了背面剔除,所以看不到")
        }
        document.getElementById("front").onclick = (e) => {
            // 相机在红色矩形的前面,可以看到
            viewMatrix = lookAt([0, 0, 1], [0, 0, 0], [0, 1, 0])
            gl.uniformMatrix4fv(uViewMatrix, false, viewMatrix);
            alert("相机在(0,0,-1)处看向(0,0,0)处,相机在红色矩形的前面,所以可以看到")
        }

        function render() {
            gl.clearColor(0.0, 0.0, 0.0, 1.0);
            gl.clear(gl.COLOR_BUFFER_BIT);

            gl.enable(gl.DEPTH_TEST);       // 开启深度测试,防止面重叠
            gl.enable(gl.CULL_FACE);        // 开启背面剔除
            gl.cullFace(gl.BACK);           // 剔除背面

            gl.bindVertexArray(vao);
            gl.drawArrays(gl.TRIANGLES, 0, 6);

            requestAnimationFrame(render);
        }

        render();
    </script>
</body>

</html>

在这里插入图片描述

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/906589.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

react使用Fullcalendar 实战用法

使用步骤请参考&#xff1a;react使用Fullcalendar 卡片式的日历&#xff1a; 需求图&#xff1a; 卡片式的日历&#xff0c;其实我是推荐 antd的&#xff0c;我两个都写了一下都能实现。 antd 的代码&#xff1a; antd的我直接用的官网示例&#xff1a;antd 日历示例 i…

基于SpringBoot+Vue实现智能停车收费系统

作者简介&#xff1a;Java领域优质创作者、CSDN博客专家 、CSDN内容合伙人、掘金特邀作者、阿里云博客专家、51CTO特邀作者、多年架构师设计经验、多年校企合作经验&#xff0c;被多个学校常年聘为校外企业导师&#xff0c;指导学生毕业设计并参与学生毕业答辩指导&#xff0c;…

ApsaraMQ Serverless 能力再升级,事件驱动架构赋能 AI 应用

本文整理于 2024 年云栖大会阿里云智能集团高级技术专家金吉祥&#xff08;牟羽&#xff09;带来的主题演讲《ApsaraMQ Serverless 能力再升级&#xff0c;事件驱动架构赋能 AI 应用》 云消息队列 ApsaraMQ 全系列产品 Serverless 化&#xff0c;支持按量付费、自适应弹性、跨可…

基于SpringBoot+Vue实现高校毕业与学位资格审查系统

作者简介&#xff1a;Java领域优质创作者、CSDN博客专家 、CSDN内容合伙人、掘金特邀作者、阿里云博客专家、51CTO特邀作者、多年架构师设计经验、多年校企合作经验&#xff0c;被多个学校常年聘为校外企业导师&#xff0c;指导学生毕业设计并参与学生毕业答辩指导&#xff0c;…

0.STM32F1移植到F0的各种经验总结

1.结构体的声明需放在函数的最前面 源代码&#xff1a; /*开启时钟*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); //开启USART1的时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟/*GPIO初始化*/GPIO_InitTypeDef GPIO_InitStructu…

RDT——清华开源的双臂机器人扩散大模型:先预训练后微调,支持语言、图像、动作多种输入

第一部分 清华开源全球最大双臂机器人扩散大模型RDT 2.1 什么是RDT 2.1.1 RDT推出的背景及其与以前工作的对比 受到最近在单手操作方面尝试的启发&#xff08;Brohan等&#xff0c;2023&#xff1b;Kim等&#xff0c;2024&#xff09;&#xff0c;清华一研究团队推出了RDT&a…

C++基础三(构造函数,形参默认值,函数重载,单例模式,析构函数,内联函数,拷贝构造函数)

C有六个默认函数&#xff0c;分别是&#xff1a; 1、默认构造函数; 2、默认拷贝构造函数; 3、默认析构函数; 4、赋值运算符; 5、取址运算符; 6、取址运算符const; 构造函数 构造函数(初始化类成员变量)&#xff1a; 1、属于类的成员函数之一 …

「DSA」C++排序算法 之 选择排序_Selection Sort_O(n²)

✨博客主页何曾参静谧的博客&#x1f4cc;文章专栏「C/C」C/C程序设计&#x1f4da;全部专栏「VS」Visual Studio「C/C」C/C程序设计「UG/NX」BlockUI集合「Win」Windows程序设计「DSA」数据结构与算法「UG/NX」NX二次开发「QT」QT5程序设计「File」数据文件格式「PK」Parasoli…

完美洗牌的秘密(十三)——(反)完美洗牌第二定理的应用(16张的Anti faro周期魔术)...

早点关注我&#xff0c;精彩不错过&#xff01; 在前面的文章中&#xff0c;我们介绍了&#xff08;反&#xff09;完美洗牌定理的应用的海量魔术&#xff0c;详情请戳&#xff1a; 完美洗牌的秘密&#xff08;十二&#xff09;——反完美洗牌定理的应用扩展&#xff08;三叠发…

TOEIC 词汇专题:旅游计划篇

TOEIC 词汇专题&#xff1a;旅游计划篇 制定旅行计划时&#xff0c;尤其是跨国旅游&#xff0c;会涉及到很多独特的英语词汇。以下是与“旅游计划”相关的托业词汇&#xff0c;帮助你更加自如地规划行程。 1. 旅行服务和优惠 出发前了解一下与服务和优惠相关的常用词汇&#…

PVE定时开启关闭虚拟机,实现PVE中群晖虚拟机的定时开机和关闭

如果在PVE中安装了群晖&#xff0c;又不想每天关闭PVE(不在家&#xff0c;怕服务器起不来)&#xff0c;因此想每天定时关闭开启黑群晖和其他虚拟机释放资源。 在网上查了很多&#xff0c;说在crontab添加命令 00 2 * * * pvesh create /nodes/pve/qemu/102/status/stop 00 6 …

JDBC/ODBC—数据库连接API概述

JDBC/ODBC概述 在数据库连接领域&#xff0c;有两种广泛使用的技术&#xff1a;ODBC&#xff08;Open Database Connectivity - 开放数据库连接&#xff09;和 JDBC&#xff08;Java Database Connectivity - Java 数据库连接&#xff09;。 一、什么是 ODBC&#xff1f; Ope…

2024年NSSCTF秋季招新赛-WEB

The Beginning F12看源码&#xff0c;有flag http标头 黑吗喽 题目说要在发售时的0点0分&#xff0c;所以添加标头data Date: Tue, 20 Aug 2024 00:00:00 GMT然后改浏览器头 User-Agent: BlackMonkey曲奇就是Cookie cookieBlackMonkey这个一般就是Referer Referer:wukon…

后台管理系统的通用权限解决方案(十一)SpringBoot的统一异常处理

文章目录 1 统一异常处理介绍2 统一异常处理案例 1 统一异常处理介绍 在实际项目中&#xff0c;不可避免需要处理各种异常。如果每个都单独处理&#xff0c;代码中则会出现大量的try {...} catch {...} finally {...}代码块&#xff0c;不仅有大量的冗余代码&#xff0c;而且还…

Axure使用动态面板制作新闻栏目高级交互

亲爱的小伙伴&#xff0c;在您浏览之前&#xff0c;烦请关注一下&#xff0c;在此深表感谢&#xff01; 课程主题&#xff1a;使用动态面板制作新闻栏目 主要内容&#xff1a;动态面板State切换、控制&#xff1b;动态面板滚动设置&#xff1b;设置选中 应用场景&#xff1a…

android数组控件Textview

说明&#xff1a;android循环控件&#xff0c;注册和显示内容 效果图&#xff1a; step1: E:\projectgood\resget\demozz\IosDialogDemo-main\app\src\main\java\com\example\iosdialogdemo\TimerActivity.java package com.example.iosdialogdemo;import android.os.Bundl…

2024年10月文章一览

2024年10月编程人总共更新了21篇文章&#xff1a; 1.2024年9月文章一览 2.《Programming from the Ground Up》阅读笔记&#xff1a;p147-p180 3.《Programming from the Ground Up》阅读笔记&#xff1a;p181-p216 4.《Programming from the Ground Up》阅读笔记&#xff…

List 列表基础用法

List 列表基础用法 列表可以完成大多数集合类的数据结构实现。列表中元素的类型可以不相同&#xff0c;它支持数字&#xff0c;字符串甚至可以包含列表&#xff08;所谓嵌套&#xff09;。 列表是写在方括号 [] 之间、用逗号分隔开的元素列表。 和字符串一样&#xff0c;列表…

企业如何通过架构蓝图实现数字化转型

数字化转型的关键——架构蓝图的力量 在当今的商业世界&#xff0c;数字化转型已经不再是一个选择&#xff0c;而是企业生存与发展不可回避的战略行动。企业希望通过数字化提高效率、增强灵活性&#xff0c;并为客户提供更好的体验。然而&#xff0c;数字化转型不仅仅涉及技术…

数字马力二面面试总结

24.03.07数字马力二面面试总结 前段时间找工作,做的一些面试笔记总结 大家有面试录音或者记录的也可以发给我,我来整理答案呀 数字马力二面面试总结 24.03.07数字马力二面面试总结你可以挑一个你的最有挑战性的,有难度的,最具有复杂性的项目,可以简单说一下。有没有和算…