小白学webgl合集-绘制有透视颜色不一样的立方体

news2024/7/2 20:48:08

效果

原理

结合透视矩阵和视觉矩阵进行绘制

知识点

01透视矩阵

透视矩阵将视图空间中的坐标转换为裁剪空间中的坐标,使得更远的物体看起来更小。

function perspectiveMatrix(fov, aspect, near, far) {
  const f = 1.0 / Math.tan(fov / 2);
  const nf = 1 / (near - far);
  
  return new Float32Array([
    f / aspect, 0, 0, 0,
    0, f, 0, 0,
    0, 0, (far + near) * nf, -1,
    0, 0, (2 * far * near) * nf, 0
  ]);
}
参数解释

函数 perspectiveMatrix 创建并返回一个透视投影矩阵,用于3D图形的透视投影。以下是每个参数的详细说明:

  1. fov(Field of View, 视野)

    • 视野角度,即观察者在垂直方向上能够看到的视角。通常以弧度表示,角度单位可以通过 Math.PI / 180 转换为弧度。
    • 例如,45 * Math.PI / 180 表示45度的视角。
  2. aspect(Aspect Ratio, 纵横比)

    • 视图的宽高比,即视图的宽度除以高度。它决定了横向和纵向的缩放比例。
    • 例如,如果视口的宽度是800像素,高度是600像素,则 aspect = 800 / 600 = 4 / 3。
  3. near(Near Clipping Plane, 近裁剪平面)

    • 近裁剪平面的距离,即从观察者到最近可见物体的距离。该值必须大于0。
    • 例如,设置为0.1表示从0.1单位距离开始可见物体。
  4. far(Far Clipping Plane, 远裁剪平面)

    • 远裁剪平面的距离,即从观察者到最远可见物体的距离。该值必须大于 near 值。
    • 例如,设置为100.0表示到100单位距离结束可见物体。

02视图矩阵

视图矩阵将世界空间中的坐标转换为视图空间中的坐标,表示相机的视点。

function lookAtMatrix(eye, center, up) {
  const zAxis = normalize(subtractVectors(eye, center));
  const xAxis = normalize(cross(up, zAxis));
  const yAxis = normalize(cross(zAxis, xAxis));

  return new Float32Array([
    xAxis[0], yAxis[0], zAxis[0], 0,
    xAxis[1], yAxis[1], zAxis[1], 0,
    xAxis[2], yAxis[2], zAxis[2], 0,
    -dot(xAxis, eye), -dot(yAxis, eye), -dot(zAxis, eye), 1
  ]);
}

function normalize(v) {
  const length = Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
  return [v[0] / length, v[1] / length, v[2] / length];
}

function subtractVectors(a, b) {
  return [a[0] - b[0], a[1] - b[1], a[2] - b[2]];
}

function cross(a, b) {
  return [
    a[1] * b[2] - a[2] * b[1],
    a[2] * b[0] - a[0] * b[2],
    a[0] * b[1] - a[1] * b[0]
  ];
}

function dot(a, b) {
  return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
}
参数解释

函数 lookAtMatrix 用于生成一个视图矩阵,用于将场景从世界空间转换到观察者空间。它通常用于实现相机的视图转换,使得相机看向特定的方向。这个函数的参数如下:

  1. eye

    • 相机的位置,表示观察者所在的点。
    • 例如 [1, 1, 1] 表示相机位于 (1, 1, 1) 的位置。
  2. center

    • 视点,表示相机所看向的目标点。
    • 例如 [0, 0, 0] 表示相机看向 (0, 0, 0) 的位置。
  3. up

    • 上方向向量,表示哪一个方向是相机的“上”方向。通常为 [0, 1, 0],表示正Y方向。
函数 lookAtMatrix 用于生成一个视图矩阵,用于将场景从世界空间转换到观察者空间。它通常用于实现相机的视图转换,使得相机看向特定的方向。这个函数的参数如下:

eye:

相机的位置,表示观察者所在的点。
例如 [1, 1, 1] 表示相机位于 (1, 1, 1) 的位置。
center:

视点,表示相机所看向的目标点。
例如 [0, 0, 0] 表示相机看向 (0, 0, 0) 的位置。
up:

上方向向量,表示哪一个方向是相机的“上”方向。通常为 [0, 1, 0],表示正Y方向。

逻辑

设置透视矩阵和视觉矩阵

 代码

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>立方体绘制</title>
    <style>
      * {
        margin: 0;
        padding: 0;
      }
      canvas {
        margin: 50px auto 0;
        display: block;
        background: yellow;
      }
    </style>
    <script src="test.js"></script>
  </head>
  <body>
    <canvas id="canvas" width="400" height="400"></canvas>
    <script>
      // 获取
      const canvas = document.getElementById('canvas')
      const gl = canvas.getContext('webgl')
      // 定义片源着色器和顶点着色器
      const vsSource = `
        attribute vec4 apos;
        attribute vec4 acolor;
        varying vec4 vcolor;
        uniform mat4 u_projectionMatrix;
      uniform mat4 u_viewMatrix;
        void main() {
          gl_Position =u_projectionMatrix * u_viewMatrix * apos;
          vcolor = acolor;
        }
      `
      const fsSource = `
        precision mediump float;
        varying vec4 vcolor;
        void main() {
          gl_FragColor = vcolor;
        }
      `
      //   初始化webgl
      const program = initShader(gl, vsSource, fsSource)
      gl.useProgram(program)
      // 获取 uniform 位置
      const u_projectionMatrixLocation = gl.getUniformLocation(program, 'u_projectionMatrix')
      const u_viewMatrixLocation = gl.getUniformLocation(program, 'u_viewMatrix')
      // 创建透视矩阵和视图矩阵
      const fov = Math.PI / 4 // 45度视角
      const aspect = canvas.width / canvas.height
      const near = 0.1
      const far = 10.0
      // lookAtMatrix 函数通过计算相机的位置、目标点和上方向,生成一个视图矩阵
      const projectionMatrix = perspectiveMatrix(fov, aspect, near, far)
      const viewMatrix = lookAtMatrix([3, 1, 7], [0, 0, 0], [0, 1, 0])
      function lookAtMatrix(eye, center, up) {
        const zAxis = normalize(subtractVectors(eye, center))
        const xAxis = normalize(cross(up, zAxis))
        const yAxis = normalize(cross(zAxis, xAxis))

        return new Float32Array([
          xAxis[0],
          yAxis[0],
          zAxis[0],
          0,
          xAxis[1],
          yAxis[1],
          zAxis[1],
          0,
          xAxis[2],
          yAxis[2],
          zAxis[2],
          0,
          -dot(xAxis, eye),
          -dot(yAxis, eye),
          -dot(zAxis, eye),
          1
        ])
      }

      function normalize(v) {
        const length = Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2])
        return [v[0] / length, v[1] / length, v[2] / length]
      }

      function subtractVectors(a, b) {
        return [a[0] - b[0], a[1] - b[1], a[2] - b[2]]
      }

      function cross(a, b) {
        return [a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]]
      }

      function dot(a, b) {
        return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]
      }

      function perspectiveMatrix(fov, aspect, near, far) {
        const f = 1.0 / Math.tan(fov / 2)
        const nf = 1 / (near - far)

        return new Float32Array([f / aspect, 0, 0, 0, 0, f, 0, 0, 0, 0, (far + near) * nf, -1, 0, 0, 2 * far * near * nf, 0])
      }

      // 设置投影矩阵和视图矩阵
      gl.uniformMatrix4fv(u_projectionMatrixLocation, false, projectionMatrix)
      gl.uniformMatrix4fv(u_viewMatrixLocation, false, viewMatrix)
      // 创建缓存区
      function initBuffer(gl, program) {
        const aposLocation = gl.getAttribLocation(program, 'apos')
        const acolorLocation = gl.getAttribLocation(program, 'acolor')
        const verticesColors = new Float32Array([
          // 前面 (红色)
          -0.5,
          -0.5,
          0.5,
          1,
          0,
          0,
          1, // 左下
          0.5,
          -0.5,
          0.5,
          1,
          0,
          0,
          1, // 右下
          0.5,
          0.5,
          0.5,
          1,
          0,
          0,
          1, // 右上
          -0.5,
          0.5,
          0.5,
          1,
          0,
          0,
          1, // 左上

          // 后面 (绿色)
          -0.5,
          0.5,
          -0.5,
          0,
          1,
          0,
          1, // 左上
          -0.5,
          -0.5,
          -0.5,
          0,
          1,
          0,
          1, // 左下
          0.5,
          -0.5,
          -0.5,
          0,
          1,
          0,
          1, // 右下
          0.5,
          0.5,
          -0.5,
          0,
          1,
          0,
          1, // 右上

          // 左面 (蓝色)
          -0.5,
          -0.5,
          -0.5,
          0,
          0,
          1,
          1, // 后下
          -0.5,
          0.5,
          -0.5,
          0,
          0,
          1,
          1, // 后上
          -0.5,
          0.5,
          0.5,
          0,
          0,
          1,
          1, // 前上
          -0.5,
          -0.5,
          0.5,
          0,
          0,
          1,
          1, // 前下

          // 右面 (洋红色)
          0.5,
          -0.5,
          -0.5,
          1,
          0,
          1,
          1, // 后下
          0.5,
          0.5,
          -0.5,
          1,
          0,
          1,
          1, // 后上
          0.5,
          0.5,
          0.5,
          1,
          0,
          1,
          1, // 前上
          0.5,
          -0.5,
          0.5,
          1,
          0,
          1,
          1, // 前下

          // 顶面 (黄色)
          -0.5,
          0.5,
          0.5,
          1,
          1,
          0,
          1, // 前左
          0.5,
          0.5,
          0.5,
          1,
          1,
          0,
          1, // 前右
          0.5,
          0.5,
          -0.5,
          1,
          1,
          0,
          1, // 后右
          -0.5,
          0.5,
          -0.5,
          1,
          1,
          0,
          1, // 后左

          // 底面 (青色)
          -0.5,
          -0.5,
          0.5,
          0,
          1,
          1,
          1, // 前左
          0.5,
          -0.5,
          0.5,
          0,
          1,
          1,
          1, // 前右
          0.5,
          -0.5,
          -0.5,
          0,
          1,
          1,
          1, // 后右
          -0.5,
          -0.5,
          -0.5,
          0,
          1,
          1,
          1 // 后左
        ])

        const indices = new Uint16Array([
          // 前面
          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
        ])

        const vertexColorBuffer = gl.createBuffer()
        gl.bindBuffer(gl.ARRAY_BUFFER, vertexColorBuffer)
        gl.bufferData(gl.ARRAY_BUFFER, verticesColors, gl.STATIC_DRAW)

        const indexBuffer = gl.createBuffer()
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer)
        gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW)

        const FSIZE = verticesColors.BYTES_PER_ELEMENT
        // 启用顶点属性并指向顶点缓冲区中的数据
        gl.vertexAttribPointer(aposLocation, 3, gl.FLOAT, false, FSIZE * 7, 0)
        gl.enableVertexAttribArray(aposLocation)

        gl.vertexAttribPointer(acolorLocation, 4, gl.FLOAT, false, FSIZE * 7, FSIZE * 3)
        gl.enableVertexAttribArray(acolorLocation)
      }
      initBuffer(gl, program)
      // 启用深度测试
      // 清除缓冲区时同时清除颜色缓冲区和深度缓冲区:
      gl.enable(gl.DEPTH_TEST)
      gl.clearColor(0, 0, 0, 1)
      gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
      gl.drawElements(gl.TRIANGLES, 36, gl.UNSIGNED_SHORT, 0)
    </script>
  </body>
</html>

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++旋转点坐标计算

/// 获取A点绕B点旋转P度后的新坐标/// </summary>/// <param name"Angle">角度</param>/// <param name"CirPoint">圆心坐标</param>/// <param name"MovePoint">移动点的坐标</param>/// <param…

(单机架设教程)3D剑踪

前言 今天给大家带来一款单机游戏的架设&#xff1a;3D剑踪 如今市面上的资源参差不齐&#xff0c;大部分的都不能运行&#xff0c;本人亲自测试&#xff0c;运行视频如下&#xff1a; 3D剑踪 搭建教程 此游戏架设不需要虚拟机&#xff0c; 我们先解压 “3D剑踪.zip” &…

【ArcGIS AddIn插件】【可用于全国水旱灾害风险普查】全网最强洪水淹没分析插件-基于8邻域种子搜索算法-有源淹没分析算法

最近有很多GIS小伙伴咨询我关于基于8邻域种子搜索算法的有源淹没分析插件的使用方法及原理&#xff0c;咱们通过这篇文章给大家详细介绍下这款插件的运行机制。 一、插件类型及适用版本 本插件属于ArcGIS AddIn工具条插件&#xff0c;基于ArcGIS Engine10.2.2的开发环境开发的&…

某度,网盘免费加速,复活!

哈喽&#xff0c;各位小伙伴们好&#xff0c;我是给大家带来各类黑科技与前沿资讯的小武。 有小伙伴反馈之前如下夸克网盘脚本的加速方法失效&#xff0c;小武今天测试&#xff0c;依旧正常使用&#xff01; 百度/迅雷/夸克&#xff0c;网盘免费加速&#xff0c;已破&#xf…

最强文生图模型Stable Diffusion 3 Medium 正式开源

Stability AI 宣布 Stable Diffusion 3 Medium 现已开源&#xff0c;是 Stable Diffusion 3 系列中最新、最先进的文本生成图像 AI 模型 —— 官方声称是 “迄今为止最先进的开源模型”&#xff0c;其性能甚至超过了 Midjourney 6。 Stable Diffusion 3 Medium 模型规格参数达到…

科普文:八大排序算法(JAVA实现)+ 自制动画 (袁厨的算法小屋)

我将我仓库里的排序算法给大家汇总整理了一下&#xff0c;写的非常非常细&#xff0c;还对每个算法制作了动画&#xff0c;一定能够对大家有所帮助&#xff0c;欢迎大家阅读。另外我也对 leetcode 上面可以用排序算法秒杀的算法题进行了总结&#xff0c;会在后面的文章中进行发…

[知识点篇]《计算机组成原理》之数据信息的表示

1、数据表示的作用 &#xff08;1&#xff09;定义&#xff1a;将数据按照某种方式组织&#xff0c;以便机器硬件能直接识别和使用。现代计算机采用二进制进行数据表示。 &#xff08;2&#xff09;数据表示考虑因素&#xff1a; 数据的类型&#xff1a; 数值/非数值、小数、…

团队任务管理跟踪软件有哪些?分享2024年值得关注的10款

本文将分享2024年值得关注的10款团队任务管理跟踪软件&#xff1a;Worktile、PingCode、Zoho Projects、Wrike、ProofHub、Connecteam、MeisterTask、Nifty、BIGContacts、Hive。 无论是小型初创企业还是庞大的跨国公司&#xff0c;高效的任务管理都能显著提升工作效率&#xf…

Linux_动、静态库

目录 一、静态库 1、静态库的概念 2、制作静态库的指令 3、制作静态库 4、链接静态库 二、动态库 1、动态库的概念 2、制作动态库的指令 3、制作动态库 4、链接动态库 5、动态库的加载 三、静态库与动态库的区别 结语 前言&#xff1a; 在Linux下大部分程序进…

颍川韩氏,来自战国七雄韩国的豪族

颍川是战国七雄韩国故土&#xff0c;韩国被秦国灭国后&#xff0c;王公贵族们除了坚决反秦的被杀了外&#xff0c;大部分都留存了下来。这些人在楚、汉反秦战争中&#xff0c;成为反秦统一战线的重要力量&#xff0c;其中两人先后被封为重新恢复的韩国的国王。 一个是横阳君韩…

240630_昇思学习打卡-Day12-Transformer中的Multiple-Head Attention

240630_昇思学习打卡-Day12-Transformer中的Multiple-Head Attention 以下为观看大佬课程及查阅资料总结所得&#xff0c;附大佬视频链接&#xff1a;Transformer中Self-Attention以及Multi-Head Attention详解_哔哩哔哩_bilibili&#xff0c;强烈建议先去看大佬视频&#xff…

【Linux】IO多路复用——select,poll,epoll的概念和使用,三种模型的特点和优缺点,epoll的工作模式

文章目录 Linux多路复用1. select1.1 select的概念1.2 select的函数使用1.3 select的优缺点 2. poll2.1 poll的概念2.2 poll的函数使用2.3 poll的优缺点 3. epoll3.1 epoll的概念3.2 epoll的函数使用3.3 epoll的优点3.4 epoll工作模式 Linux多路复用 IO多路复用是一种操作系统的…

人工智能导论速成笔记

文章目录 前言考试题型第一章、人工智能导引 (10分 )课后习题第二章、Python基础 (10分 )*文件读写NumPy的使用Python绘图基础第三章、机器学习初步(15分 )逻辑回归分类(Logistic Regression)*,3.5线性回归预测(Linear Regression)*,3.6 、3.7、 3.8聚类 3.9第四章、自然语言…

郑州高校大学智能制造实验室数字孪生可视化系统平台建设项目验收

随着制造业的转型升级&#xff0c;智能化、信息化已成为制造业发展的必然趋势。数字孪生技术作为智能制造领域的关键技术之一&#xff0c;它通过构建与实体系统相对应的虚拟模型&#xff0c;实现对实体系统的实时监测、预测和优化&#xff0c;为制造业的智能化、信息化提供了强…

叶老师的新水杯c++

题目描述 最近叶老师换了个带吸管的水杯。 贝贝发现当叶老师使用带吸管的水杯时&#xff0c;每天会喝 x 毫升的水。而使用不带吸管的水杯时&#xff0c;每天会喝 y 毫升的水。 请问在 n 天的时间内&#xff0c;叶老师喝水量的上限与下限相差多少&#xff1f; 输入 第一行为…

Advanced RAG 09:『提示词压缩』技术综述

编者按&#xff1a; 如何最大限度地发挥 LLMs 的强大能力&#xff0c;同时还能控制其推理成本&#xff1f;这是当前业界研究的一个热点课题。 针对这一问题&#xff0c;本期精心选取了一篇关于"提示词压缩"(Prompt Compression)技术的综述文章。正如作者所说&#xf…

VMware17.0 安装过程

VMware17.0 VMware 17.0 是一款功能强大的虚拟机软件&#xff0c;用于在计算机上创建和管理虚拟机。它能够同时运行多个操作系统&#xff0c;如 Windows、Linux 等&#xff0c;并且在这些虚拟机之间提供无缝的切换和共享功能。 VMware 17.0 支持最新的硬件和操作系统&#xf…

区间动态规划——最长回文子串(C++)

难得心静。 ——2024年6月30日 什么是区间动态规划&#xff1f; 区间动态规划通常以连续区间的求解作为子问题&#xff0c;例如区间 [i, j] 上的最优解用dp[i][j]表示。先在小区间上进行动态规划得到子问题的最优解&#xff0c;再利用小区间的最优解合并产生大区间的最优解。 …

ComfyUI高清放大的四种方式(工作流附件在最后)

方式一&#xff1a;Latent放大工作流 1.工作流截图 方式二&#xff1a;ESRGAN&#xff08;传统模型&#xff09;放大工作流 方式三&#xff1a;算法放大&#xff08;后期处理&#xff09;工作流 方式四&#xff1a;Ultimate SD Upscale工作流 这个方式的优势是对于显存底的用…

WP黑格导航主题BlackCandy

BlackCandy-V2.0全新升级&#xff01;首推专题区(推荐分类)更多自定义颜色&#xff01;选择自己喜欢的色系&#xff0c;焕然一新的UI设计&#xff0c;更加扁平和现代化&#xff01; WP黑格导航主题BlackCandy