WebGL笔记:图形旋转的原理和实现

旋转

1 )旋转的概念

  • 三维物体的旋转要比位移复杂一点,三维物体的旋转需要满足以下条件:
    • 旋转轴
    • 旋转方向
    • 旋转角度
  • 场景举例
    • 模型站在旋转轴的起点进行旋转
    • 模型要往左转还是往右转,就是旋转的方向
    • 模型旋转的大小就是旋转角度


2 )旋转方向的正负

  • 在webgl中,除裁剪空间之外的大部分功能都使用了右手坐标系
  • 在webgl中,可以暂且将其当成右手坐标系, 下图就是右手坐标系


  • 以上图为例

    • 当物体绕 z 轴,从x轴正半轴向y轴正半轴逆时针旋转时,是正向旋转,反之为负。
    • 当物体绕 x 轴,从y轴正半轴向z轴正半轴逆时针旋转时,是正向旋转,反之为负。
    • 当物体绕 y 轴,从z轴正半轴向x轴正半轴逆时针旋转时,是正向旋转,反之为负。
  • 如下图就是正向旋转

    • 围绕z轴(骑着z轴),从x轴到y轴逆时针转动就是正向旋转


旋转公式

  • 如下,让顶点围绕 z 轴旋转的场景


  • 已知
    • 点A的位置是(ax,ay,az)
    • 点A要围绕z轴旋转β度,转到点B的位置
  • 求:点A旋转后的bx、by位置
    • 因为∠β是已知的,∠α 可以通过点 A 得出
    • 所以我们可以得出
      ∠xOB = α + β
      
    • 那我们通过三角函数就可以推出bx、by
    • 设 ∠xOB = θ,则:
      bx = cosθ * |OA|
      by = sinθ * |OA| // 注意这里因为是旋转, 所以 |OA| === |OB|, 统一用 |OA|来表示
      
    • 上面的|OA|是点O到点A的距离,可以直接用点A求出
      |OA| = Math.sqrt(ax * ax + ay * ay)
      
    • 那我们接下来只需要知道cosθ和sinθ的值即可
    • 因为:θ = α + β
    • 所以,我们可以利用和角公式求cosθ和sinθ的值
      cosθ = cos(α + β)
      cosθ = cosα * cosβ - sinα * sinβ
      
      sinθ = sin(α + β)
      sinθ = cosβ * sinα + sinβ * cosα
      
    • 所以
      bx = cosθ * |OA|
      bx = (cosα * cosβ - sinα * sinβ) * |OA|
      bx = cosα * cosβ * |OA| - sinα * sinβ * |OA|
      
      by = sinθ * |OA|
      by = (cosβ * sinα + sinβ * cosα) * |OA|
      by = cosβ * sinα * |OA| + sinβ * cosα * |OA|
      
    • 因为
      cosα * |OA| = ax
      sinα * |OA| = ay
      
    • 所以我们可以简化bx、by的公式
      bx = ax * cosβ - ay * sinβ
      by = ay * cosβ + ax * sinβ
      
    • 上面的bx、by就是我们要求的答案

在着色器中旋转

  • 可以直接在着色器里写旋转公式

    <script id="vertexShader" type="x-shader/x-vertex">
        attribute vec4 a_Position;
        float angle = radians(80.0);
        float sinB = sin(angle);
        float cosB = cos(angle);
        void main() {
            gl_Position.x = a_Position.x * cosB - a_Position.y * sinB;
            gl_Position.y = a_Position.y * cosB + a_Position.x * sinB;
            gl_Position.z = a_Position.z;
            gl_Position.w = 1.0;
        }
    </script>
    
  • radians(float degree) 将角度转弧度

  • sin(float angle) 正弦

  • cos(float angle) 余弦

用js旋转图形

我们将顶点着色器里的正弦值和余弦值暴露给js,便可以用js旋转图形了

<script id="vertexShader" type="x-shader/x-vertex">
    attribute vec4 a_Position;
    uniform float u_SinB;
    uniform float u_CosB;
    void main() {
        gl_Position.x = a_Position.x * u_CosB-a_Position.y * u_SinB;
        gl_Position.y = a_Position.y * u_CosB+a_Position.x * u_SinB;
        gl_Position.z = a_Position.z;
        gl_Position.w = 1.0;
    }
</script>
  • 在js 中修改uniform 变量

    const u_SinB = gl.getUniformLocation(gl.program, 'u_SinB');
    const u_CosB = gl.getUniformLocation(gl.program, 'u_CosB');
    const angle = 0.3;
    gl.uniform1f(u_SinB, Math.sin(angle));
    gl.uniform1f(u_CosB, Math.cos(angle));
    
  • 之后让图形转起来

    !(function ani() {
        angle += 0.01;
        gl.uniform1f(u_SinB, Math.sin(angle));
        gl.uniform1f(u_CosB, Math.cos(angle));
        gl.clear(gl.COLOR_BUFFER_BIT);
        gl.drawArrays(gl.TRIANGLES, 0, 3);
        requestAnimationFrame(ani);
    })()
    

完整代码

<canvas id="canvas"></canvas>
<script id="vertexShader" type="x-shader/x-vertex">
  attribute vec4 a_Position;
  uniform float u_SinB;
  uniform float u_CosB;
  void main() {
      gl_Position.x = a_Position.x * u_CosB - a_Position.y * u_SinB;
      gl_Position.y = a_Position.y * u_CosB + a_Position.x * u_SinB;
      gl_Position.z = a_Position.z;
      gl_Position.w = 1.0;
  }
</script>
<script id="fragmentShader" type="x-shader/x-fragment">
  void main() {
      gl_FragColor = vec4(1.0,1.0,0.0,1.0);
  }
</script>
<script type="module">
  import { initShaders } from './utils.js';
  const canvas = document.getElementById('canvas');
  canvas.width = window.innerWidth;
  canvas.height = window.innerHeight;
  const gl = canvas.getContext('webgl');

  const vsSource = document.getElementById('vertexShader').innerText;
  const fsSource = document.getElementById('fragmentShader').innerText;
  initShaders(gl, vsSource, fsSource);

  const vertices = new Float32Array([
    0.0, 0.1,
    -0.1, -0.1,
    0.1, -0.1
  ]);

  const vertexBuffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
  gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
  const a_Position = gl.getAttribLocation(gl.program, 'a_Position');
  gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);
  gl.enableVertexAttribArray(a_Position);

  // 获取Uniform变量
  const u_SinB = gl.getUniformLocation(gl.program, 'u_SinB')
  const u_CosB = gl.getUniformLocation(gl.program, 'u_CosB')
  // 修改uniform 变量
  let angle = 0.3
  gl.uniform1f(u_SinB, Math.sin(angle))
  gl.uniform1f(u_CosB, Math.cos(angle))

  gl.clearColor(0.0, 0.0, 0.0, 1.0);
  gl.clear(gl.COLOR_BUFFER_BIT);
  gl.drawArrays(gl.TRIANGLES, 0, 3);

  !(function ani() {
    angle += 0.01;
    gl.uniform1f(u_SinB, Math.sin(angle));
    gl.uniform1f(u_CosB, Math.cos(angle));
    gl.clear(gl.COLOR_BUFFER_BIT);
    gl.drawArrays(gl.TRIANGLES, 0, 3);
    requestAnimationFrame(ani);
  })()
</script>

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

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

相关文章

基于SpringBoot的公益慈善平台

✌全网粉丝20W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取项目下载方式&#x1f345; 一、项目背景介绍&#xff1a; 基于SpringBoot的公益…

使用Python实现SVM来解决二分类问题

下面是一个使用Python实现SVM来解决二分类问题的例子&#xff1a; # 导入所需的库 from sklearn.datasets import make_blobs from sklearn.model_selection import train_test_split from sklearn.svm import SVC import matplotlib.pyplot as plt# 生成一个二分类数据集 X, …

ESP32-Web-Server 实战编程-通过网页控制设备多个 GPIO

ESP32-Web-Server 实战编程-通过网页控制设备多个 GPIO 概述 上节 ESP32-Web-Server 实战编程-通过网页控制设备的 GPIO 讲述了如何通过网页控制一个 GPIO。本节实现在网页上控制多个 GPIO。 示例解析 前端设计 前端代码建立了四个 GPIO&#xff0c;如下死 GPIO 2 在前端的…

STM32F407-14.3.5-01捕获_比较通道

捕获/比较通道 每一个捕获/比较通道都是围绕着一个捕获/比较寄存器(包含影子寄存器) 包括: 捕获的输入部分(数字滤波、多路复用和预分频器)&#xff0c; 输出部分(比较器和输出控制)。 中文参考手册中框图分成了三大模块, 把框图合并成了一个整体,以便更好的理解捕获输…

深度学习之基于百度飞桨PaddleOCR图像字符检测识别系统

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介主要特点使用步骤 二、功能三、系统四. 总结 一项目简介 # Introduction to PaddleOCR Image Character Detection and Recognition System Based on Baidu…

Java---权限修饰符、final、static

文章目录 1. 权限修饰符2. final(最终态)3. static(静态) 1. 权限修饰符 修饰符同一个类中同一个包中的子类和无关类不同包的子类不同包的无关类private√默认√√protected√√√public√√√√ 2. final(最终态) 1. final关键字是最终的意思&#xff0c;可以修饰成员方法、…

Appium PO模式UI自动化测试框架——设计与实践

1. 目的 相信做过测试的同学都听说过自动化测试&#xff0c;而UI自动化无论何时对测试来说都是比较吸引人的存在。相较于接口自动化来说&#xff0c;它可以最大程度的模拟真实用户的日常操作与特定业务场景的模拟&#xff0c;那么存在即合理&#xff0c;自动化UI测试自然也是广…

《融合SCADA系统数据的天然气管道泄漏多源感知技术研究》误报数据识别模型开发

数据处理不作表述。因为我用的是处理后的数据&#xff0c;数据点这。 文章目录 工作内容1CC040VFD电流VFD转速压缩机转速反馈进出口差压 紧急截断阀开到位进出电动阀开到位发球筒电筒阀开到位收球筒电动阀开到位电动阀2005开到位越站阀开到位 工作内容2工作内容3 工作内容1 任…

金字塔原理 读书笔记

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言第1篇 表达的逻辑第1章 为什么要用金字塔结构归类分组&#xff0c;将思想组织成金字塔自上而下表达&#xff0c;结论先行自下而上思考&#xff0c;总结概括 第2…

Spark---资源、任务调度

一、Spark资源调度源码 1、Spark资源调度源码过程 Spark资源调度源码是在Driver启动之后注册Application完成后开始的。Spark资源调度主要就是Spark集群如何给当前提交的Spark application在Worker资源节点上划分资源。Spark资源调度源码在Master.scala类中的schedule()中进行…

基于SSM的云鑫曦科技办公自动化管理系统设计与实现

基于SSM的云鑫曦科技办公自动化管理系统设计与实现 摘 要: 随着时代的发展&#xff0c;单位办公方式逐渐从传统的线下纸张办公转向了使用个人pc的线上办公&#xff0c;办公效率低下的传统纸质化办公时代的淘汰&#xff0c;转型到信息化办公时代&#xff0c;面对当今数据逐渐膨…

奇数求和(C++)

系列文章目录 进阶的卡莎C++_睡觉觉觉得的博客-CSDN博客数1的个数_睡觉觉觉得的博客-CSDN博客双精度浮点数的输入输出_睡觉觉觉得的博客-CSDN博客足球联赛积分_睡觉觉觉得的博客-CSDN博客大减价(一级)_睡觉觉觉得的博客-CSDN博客小写字母的判断_睡觉觉觉得的博客-CSDN博客纸币(…

Keil5 debug

目录 debug调试功能 基本功能&#xff1a; 程序复位&#xff1a;Reset 运行&#xff1a;Run 停止&#xff1a;Stop 断点调试&#xff08;Breakpoint Debugging&#xff09; 单步调试&#xff1a; 单步调试:Step 单步跳过调试&#xff1a;Step Over&#xff1a; 单步返…

vue项目中使用jsonp跨域请求百度联想接口

一. 内容简介 vue项目中使用jsonp跨域请求百度联想接口 二. 软件环境 2.1 Visual Studio Code 1.75.0 2.2 chrome浏览器 2.3 node v18.14.0 三.主要流程 3.1 代码 核心代码 // 这个是请求函数doLeno() {// 挂载回调函数&#xff0c;不挂载&#xff0c;会报不存在window…

算法中的时间复杂度,空间复杂度

一、前言 算法&#xff08;Algorithm&#xff09;是指用来操作数据、解决程序问题的一组方法。对于同一个问题&#xff0c;使用不同的算法&#xff0c;也许最终得到的结果是一样的&#xff0c;但在过程中消耗的资源和时间却会有很大的区别 衡量不同算法之间的优劣主要是通过时…

【动态规划】求最长递增子序列问题

目录 问题描述递推关系建立递推关系的思路约束条件:以 s [ k ] s[k] s[k] 结尾约束条件:以 s [ k ] s[k] s[k] 开头约束条件:增加子问题参数&#xff08;前缀&#xff09;约束条件:增加子问题参数&#xff08;后缀&#xff09;约束条件:LIS长度为k且末尾元素最小 运行实例 问…

MySQL -DDL 及表类型

DDL 创建数据库 CREATE DATABASE [IF NOT EXISTS] db_name [create_specification [, create_specification] ...] create_specification:[DEFAULT] CHARACTER SET charset_name [DEFAULT] COLLATE collation_name 1.CHARACTER SET&#xff1a…

Java后端开发——MVC商品管理程序

Java后端开发——MVC商品管理程序 本篇文章内容主要有下面几个部分&#xff1a; MVC架构介绍项目环境搭建商品管理模块Servlet代码重构BaseServlet文件上传 MVC 是模型-视图-控制器&#xff08;Model-View-Controller&#xff09;&#xff0c;它是一种设计模式&#xff0c;也…

.net7.0中把exe和dll分开打包

之前写过 C#把dll分别放在指定的文件夹_wpf core dll 放文件夹-CSDN博客 C#把dll打包到exe_c# 打包exe_故里2130的博客-CSDN博客 这都是老技术了&#xff0c;可以进行参考。 现在的.netcore系列有单独支持把exe和dll分开打包的功能了&#xff0c;当然也支持.net7.0和.net8.…

开源堡垒机Jumpserver

文章目录 开源堡垒机JumpserverJumpserver介绍安装环境部署安装jumpserver访问jumpserver的web界面 开源堡垒机Jumpserver Jumpserver介绍 Jumpserver 是全球首款完全开源的堡垒机&#xff0c;使用 GNU GPL v2.0 开源协议&#xff0c;是符合 4A 的运维安全审计系统。 Jumpse…