webgl变换:深入图形平移
在以前的文章里,不管是绘制图形,绘制点亦或者是改变色值,所有的内容都是静态的。
在 webgl 里,图形的运动分为 平移、旋转、缩放 三种类型。
接下来,我们会从零基础开始,一点一点来深入了解图形如何进行运动。
首先来从零开始了解下图形的平移
1. 图形平移
首先我们来看如何实现图形的平移操作。
平移的操作就是将图形的原始坐标加上对应的移动距离。首先来看下平移的实现
const vertexShaderSource = "" +
"attribute vec4 apos;" + // 定义一个坐标
"uniform float x;" + // 处理 x 轴移动
"uniform float y;" + // 处理 y 轴移动
"void main(){" +
" gl_Position.x = apos.x + x;" +
" gl_Position.y = apos.y + y;" +
" gl_Position.z = 0.0;" + // z轴固定
" gl_Position.w = 1.0;" +
"}";
const fragmentShaderSource = "" +
"void main(){" +
" gl_FragColor = vec4(1.0,0.0,0.0,1.0);" +
"}";
// initShader已经实现了很多次,本次就不再赘述了
const program = initShader(gl,vertexShaderSource,fragmentShaderSource);
const buffer = gl.createBuffer();
const data = new Float32Array([
0.0,0.0,
-0.5,-0.5,
0.5,-0.5,
]);
gl.bindBuffer(gl.ARRAY_BUFFER,buffer);
gl.bufferData(gl.ARRAY_BUFFER,data,gl.STATIC_DRAW);
const aposlocation = gl.getAttribLocation(program,'apos');
const xlocation = gl.getUniformLocation(program,'x');
const ylocation = gl.getUniformLocation(program,'y');
gl.vertexAttribPointer(aposlocation,2,gl.FLOAT,false,0,0);
gl.enableVertexAttribArray(aposlocation);
let x = 0.0;
let y = 0.0;
function run () {
gl.uniform1f(xlocation,x += 0.01);
gl.uniform1f(ylocation,y += 0.01);
gl.drawArrays(gl.TRIANGLES,0,3);
// 使用此方法实现一个动画
requestAnimationFrame(run)
}
run()
解释:
- 首先声明一个变量
x和变量y,用来处理 x轴 和 y轴 的坐标。这里使用的是uniform变量,因为平移的操作对于图形上的所有顶点都有影响。 - 通过
gl_Position.[xyzw]来分别设置x、y、z、w的值。用于改变图形位置。 - 使用
gl.uniform1f来为 x 和 y 赋值 - 使用
requestAnimationFrame实现一个缓动动画。方便观察效果。 - 其他的操作,缓冲区,绘制,赋值,激活,
可以看到,这样处理图形移动的话很好理解,但是因为一个移动,我们声明了两个 uniform 变量来实现。并且分开设置的 xyz 坐标,非常的不方便。
所以,在处理webgl变换(平移、缩放、旋转)的时候,通常使用矩阵来实现。接下来就来看看,如何使用矩阵实现图形的平移。
2. 平移矩阵
推导平移矩阵的步骤:
- 获取平移前后的图形坐标(三维)
- 计算平移前后的差值
- 带入到平移矩阵
- 处理图形顶点
- 获得平移后的图形
2.1 平移矩阵的推导
首先让我们来看一幅图片。

这幅图片的意义就是我们将橙色的三角形移动到蓝色虚线三角形处。
移动之后的蓝色虚线三角形的三个坐标分别为
x’ = x + x1y' = y + y1z' = z + z1w=1齐次坐标为1
2.2 获得平移矩阵
在 webgl 中,通常使用矩阵来实现图形变换。下面我们来看看矩阵如何表示。

左侧是平移之前的原始坐标,中间的是一个平移矩阵,经过两者相乘,可以得到一个平移之后的坐标。
现在我们来看下平移矩阵如何计算得出
首先通过上述图片中的矩阵我们来得到几个方程式。用左侧的列分别乘矩阵的行,可以得到一下公式
ax + by + cz + w = x'ex + fy + gz + h = y'ix + jy + kz + l = z'mx + ny + oz + p = w'
公式合并:
将第一节 里的四个方程式和第二节里的四个方程式合并,可以得到如下结果:
ax + by + cz + w = x + x1':只有当a = 1,b = c = 0, w = x1的时候,等式左右两边成立ex + fy + gz + h = y + y1':只有当f = 1, e = g = 0, h = y1的时候,等式左右两边成立ix + jy + kz + l = z + z1':只有当k = 1,i = j = 0, l = z1的时候,等式左右两边成立mx + ny + oz + p = 1':只有当m = n = o = 0, p = 1的时候,等式左右两边成立
经过上述方程式,可以得到一个平移的矩阵:
| 1 0 0 x |
| 0 1 0 y |
| 0 0 1 z |
| 0 0 0 1 |
之后将平移矩阵和原始坐标相乘,就可以得到平移之后的坐标。
3. 矩阵实战
来看看使用矩阵如何处理图形的平移。
第一步,创建着色器源代码
const vertexShaderSource = "" +
"attribute vec4 apos;" +
"uniform mat4 mat;" + // 创建一个 uniform 变量,代表平移矩阵
"void main(){" +
" gl_Position = mat * apos;" + // 矩阵与原始坐标相乘
"}";
const fragmentShaderSource = "" +
"void main(){" +
" gl_FragColor = vec4(1.0,0.0,0.0,1.0);" +
"}";
第二步,创建平移矩阵
let Tx = 0.1; //x坐标的位置
let Ty = 0.1; //y坐标的位置
let Tz = 0.0; //z坐标的位置
let Tw = 1.0; //差值
const mat = new Float32Array([
1.0,0.0,0.0,0.0,
0.0,1.0,0.0,0.0,
0.0,0.0,1.0,0.0,
Tx,Ty,Tz,Tw,
]);
这里可以看到,使用的矩阵和我们推导出来的矩阵不太一样,推导的平移矩阵里 xyzw 位于矩阵的右侧,现在是位于矩阵的底部,这是为什么呢?
这是因为在 webgl 中,矩阵的使用需要按照 左上右下 的对角线做一次翻转。所以使用的矩阵,xyzw 位于底部
第三步,绘制一个三角形
const program = initShader(gl,vertexShaderSource,fragmentShaderSource);
const aposlocation = gl.getAttribLocation(program,'apos');
const data = new Float32Array([
0.0,0.0,
-.3,-.3,
.3,-.3
]);
const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER,buffer);
gl.bufferData(gl.ARRAY_BUFFER,data,gl.STATIC_DRAW);
gl.vertexAttribPointer(aposlocation,2,gl.FLOAT,false,0,0);
gl.enableVertexAttribArray(aposlocation);
gl.drawArrays(gl.TRIANGLES,0,3); // 第五步的时候会重写
第四步,获取矩阵变量,给矩阵赋值
const matlocation = gl.getUniformLocation(program,'mat');
gl.uniformMatrix4fv(matlocation,false,mat);
这里使用 gl.uniformMatrix4fv 来给矩阵赋值。
第五步,添加缓动动画
function run () {
Tx += 0.01
Ty += 0.01
const mat = new Float32Array([
1.0,0.0,0.0,0.0,
0.0,1.0,0.0,0.0,
0.0,0.0,1.0,0.0,
Tx,Ty,Tz,Tw,
]);
gl.uniformMatrix4fv(matlocation,false,mat);
gl.drawArrays(gl.TRIANGLES,0,3);
// 使用此方法实现一个动画
requestAnimationFrame(run)
}
run()
4. 完整代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<canvas id="webgl" width="500" height="500"></canvas>
<script>
const gl = document.getElementById('webgl').getContext('webgl');
const vertexShaderSource = "" +
"attribute vec4 apos;" +
"uniform mat4 mat;" +
"void main(){" +
" gl_Position = mat * apos;" +
"}";
const fragmentShaderSource = "" +
"void main(){" +
" gl_FragColor = vec4(1.0,0.0,0.0,1.0);" +
"}";
const program = initShader(gl,vertexShaderSource,fragmentShaderSource);
const aposlocation = gl.getAttribLocation(program,'apos');
const matlocation = gl.getUniformLocation(program,'mat');
const data = new Float32Array([
0.0,0.0,
-.3,-.3,
.3,-.3
]);
const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER,buffer);
gl.bufferData(gl.ARRAY_BUFFER,data,gl.STATIC_DRAW);
gl.vertexAttribPointer(aposlocation,2,gl.FLOAT,false,0,0);
gl.enableVertexAttribArray(aposlocation);
let Tx = 0.1; //x坐标的位置
let Ty = 0.1; //y坐标的位置
let Tz = 0.0; //z坐标的位置
let Tw = 1.0; //差值
function run () {
Tx += 0.01
Ty += 0.01
const mat = new Float32Array([
1.0,0.0,0.0,0.0,
0.0,1.0,0.0,0.0,
0.0,0.0,1.0,0.0,
Tx,Ty,Tz,Tw,
]);
gl.uniformMatrix4fv(matlocation,false,mat);
gl.drawArrays(gl.TRIANGLES,0,3);
// 使用此方法实现一个动画
requestAnimationFrame(run)
}
run()
function initShader(gl,vertexShaderSource,fragmentShaderSource){
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(vertexShader,vertexShaderSource);
gl.shaderSource(fragmentShader,fragmentShaderSource);
gl.compileShader(vertexShader);
gl.compileShader(fragmentShader);
const program = gl.createProgram();
gl.attachShader(program,vertexShader);
gl.attachShader(program,fragmentShader)
gl.linkProgram(program);
gl.useProgram(program);
return program;
}
</script>
</body>
</html>
至此,通过矩阵控制图形移动就全部实现完成了。
今天的分享就到这儿了,
Bye~

更多精彩内容,定制礼品图书赠送,高薪职位内推,微信搜索关注“豆皮范儿”
webgl变换:深入图形平移的更多相关文章
- webgl学习笔记三-平移旋转缩放
写在前面 建议先阅读下前面我的两篇文章. webgl学习笔记一-绘图单点 webgl学习笔记二-绘图多点 平移 1.关键点说明 顶点着色器需要加上 uniform vec4 u_Translation ...
- WebGL简易教程(五):图形变换(模型、视图、投影变换)
[toc] 1. 概述 通过之前的教程,对WebGL中可编程渲染管线的流程有了一定的认识.但是只有前面的知识还不足以绘制真正的三维场景,可以发现之前我们绘制的点.三角形的坐标都是[-1,1]之间,Z值 ...
- 《Real Time Rendering》第四章 图形变换
图形变换是一个将例如点.向量或者颜色等实体进行某种转换的操作.对于计算机图形学的先驱者,掌握图形变换是极为重要的.有了他们,你就可以对象.光源以及摄像机进行定位,变形以及动画添加.你也可以确认所有的计 ...
- WebGL高级编程:开发Web3D图形 PDF(中文版带书签)
WebGL高级编程:开发Web3D图形 目录 WebGL简介11.1 WebGL基础11.2 浏览器3D图形吸引人的原因21.3 设计一个图形API31.3.1 即时模式API31.3.2 保留模式A ...
- WebGL学习笔记(1)
基本的WebGL图形操作(详细参考教程:https://www.yiibai.com/webgl,需要1周左右熟悉webgl的对象方法以及着色器代码):绘制三角形 drawElements gl.TR ...
- WEBGL学习【八】模型视图投影矩阵
<!--探讨WEBGL中不同图形的绘制方法:[待测试2017.11.6]--> <!DOCTYPE HTML> <html lang="en"> ...
- WebGL简易教程(六):第一个三维示例(使用模型视图投影变换)
目录 1. 概述 2. 示例:绘制多个三角形 2.1. Triangle_MVPMatrix.html 2.2. Triangle_MVPMatrix.js 2.2.1. 数据加入Z值 2.2.2. ...
- 浅谈 GPU图形固定渲染管线
图形渲染管道被认为是实时图形渲染的核心,简称为管道.管道的主要功能是由给定的虚拟摄像机.三维物体.灯源.光照模型.纹理贴图或其他来产生或渲染一个二维图像.由此可见,渲染管线是实时渲染技术的底层工具.图 ...
- OpenGL基础图形编程
一.OpenGL与3D图形世界1.1.OpenGL使人们进入三维图形世界 我们生活在一个充满三维物体的三维世界中,为了使计算机能精确地再现这些物体,我们必须能在三维空间描绘这些物体.我们又生活在一个充 ...
随机推荐
- 从苏宁电器到卡巴斯基第26篇:难忘的三年硕士时光 IV
录课,录课,还是录课 开题结束以后,已经是三月的下旬,当时我在考虑要不要回家,毕竟学校这里也没什么事了,我待在学校还得付出一定的花销.后来我考虑到在家的话,只有晚上才能够录课,而在学校的话,整个白天都 ...
- 病毒木马查杀实战第021篇:Ring3层主动防御之编程实现
前言 我们这次会依据上次的内容,编程实现一个Ring3层的简单的主动防御软件.整个程序使用MFC实现,程序开始监控时,会将DLL程序注入到explorer.exe进程中,这样每当有新的进程创建,程序首 ...
- Cauchy-Binet 公式的应用
Binet-Cauchy 公式 我们知道,方阵的行列式不是方阵的线性函数,即对 \(\forall \lambda\in F,A,B\in F^{n\times n}\),有 \(det(A+B)\n ...
- SpringBoot 项目 部署 war方式
SpringBoot部署 –war方式 步骤1部署方式 Springboot 和我们之前学习的web 应用程序不一样,其本质上是一个 Java 应用程序,那么又如何部署呢? 通常来说,Springbo ...
- pr恢复工作区
当工作区操作的位置很乱时 平时如果关闭的窗口,可以在窗口中查看 也可以选择新建工作区,保存成一个自己所需工作区
- CCNA 第二章 以太网回顾
1:半双工和全双工 (1):半双工:类似于单车道: (2):全双工:类似是双向多车道: 2:思科三层模型 (1): (2):核心层.集散层(汇聚层).接入层各功能: 1:核心层:大量数据快速交换:不要 ...
- leecode之Implement strStr()
KMP算法的实现: #include <stdio.h> #include <string.h> #include <stdlib.h> int strStr(ch ...
- 使用find_if算法搜寻map的value
// // main.cpp // map_find // // Created by PKU on 14-9-8. // Copyright (c) 2014年 PKU. All rights re ...
- API网关才是大势所趋?SpringCloud Gateway保姆级入门教程
什么是微服务网关 SpringCloud Gateway是Spring全家桶中一个比较新的项目,Spring社区是这么介绍它的: 该项目借助Spring WebFlux的能力,打造了一个API网关.旨 ...
- 【转】java-selenium三种等待方式
方式1: 线程等待:Thread.sleep(xxxx) 只要在case中加入sleep就会强制等待设置的时间后才会执行之后的命令,这种等待一般适用于调试脚本的时候. java代码: //等待3秒 T ...