找回密码
 会员注册
查看: 14|回复: 0

图形渲染入门窥径(前端视角+科普向)_UTF_8

[复制链接]

1

主题

0

回帖

4

积分

新手上路

积分
4
发表于 2024-9-30 19:16:18 | 显示全部楼层 |阅读模式
/ 关于 & 介绍 /图形渲染的内容在前端的整个知识体系当中占据着不可或缺的一部分,无论是数据可视化、3D 模型展示、H5 游戏开发都需要对图形渲染方面的知识有所了解。本文既从前端视角出发,来对图形渲染方面的入门知识做一些粗浅的科普,并实际动手完成在浏览器当中渲染一个简单的 3D 模型。/ 渲染 /在代码实现过程中包含许多数据模型与数学算法,为避免在开始实现之前抛出太多概念影响整体学习流程,因此只有在使用某个数据或算法时才会展开进行讲解。1 2D 色块渲染每一个 3d 模型大抵都是由许多的三角形面组成的( 哪怕有些输出的模型并非三角形面,也可以将一个多边形面转化多个三角形面,即三角形是最简单的平面,而诸多平面的组合构成一个模型 )。每一个三角形面,都是由三个顶点组成的,换句话说,一个模型就是由多个顶点组成的,所以在渲染的过程中,我们核心关注的内容,就是顶点的变化与渲染。下方是一个例子,来展示如何渲染一个简单的三角形。//一些类与方法会放到整体流程下方讲解functionrenderPlane(){//canvas基本信息获取constcanvas=document.getElementById("renderCanvas")asHTMLCanvasElement;constctx=canvas.getContext("2d");//canvas的宽高决定了绘制像素的数量constwidth=canvas.width; const height = canvas.height;//以原始坐标[-1,1]来定义随意定义一个三角形的三个顶点constv1=newVertex();//Vertex代表顶点类:存储位置信息(position)、法向量、颜色、UV坐标等信息v1.position=newVec4(0,0.2,0,1);constv2=newVertex();v2.position=newVec4(-0.2,-0.2,0,1);constv3=newVertex();v3.position=newVec4(0.2,-0.2,0,1);/**视口变换:将标准平面映射到屏幕分辨率范围之内即,将[-1,1]^2坐标映射到[0,width]*[0,height]组成的坐标系在canvas环境中,width&height即为canvas.width&canvas.height简单来说就是将某一个坐标系当中的点,映射到另一个坐标系当中*///转换为视口坐标,具体方法下方会详细阐述constviewPosition1=getViewPortPosition(v1.position);constviewPosition2=getViewPortPosition(v2.position);constviewPosition3=getViewPortPosition(v3.position); /**图片信息:ctx.createImageData(width,height)以一个width,height=1的canvas举例,其imageData为Uint8ClampedArrayImageData:{data:{0:255,R值1:0,G值2:0,B值3:255,A值},colorSpace:"srgb",颜色类型height:1,width:1,}其中data为颜色信息,具体类型为Uint8ClampedArray*/constimageData=ctx.createImageData(width,height);/**至此,我们获得了当前canvas的上所有的像素信息借助当前canvas的width,height创建一个buffer对象对于每一个实际被渲染的图像(对于canvas来说就是通过putImageData来创建的图像),都存在一个缓冲帧对象(FrameBuffer),用于存储下一次需要被渲染出来的图像数据信息FrameBuffer当中存储着与ImageData.data对应的颜色数组信息、宽高信息,初识色(清除色)通过xy来索引颜色数组中的位置,对单个像素颜色进行赋值*/constbuffer=newFrameBuffer(width,height);//初始数据buffer.setFrameBuffer(imageData.data);buffer.setClearColor(Color.BLACK);//遍历整个画布,获取对应像素坐标,进行赋值(下方会讲解优化方法)for(letx=0;x像素坐标偏移0.5以对其像素中心点constcurposition=newVec4(x+0.5,y+0.5,0,1);//通过叉乘判断当前坐标点是否位于三角形内部constisInner=isTriangleInner(curposition,viewPosition1,viewPosition2,viewPosition3);//如果当前点位于三角形内部,就给当前坐标像素赋予一个颜色isInner&buffer.setColor(x,y,Color.RED);}}//写入像素数据ctx.putImageData(imageData,0,0);};至此,我们就能绘制出一个简单的三角形了,其中仍有许多待优化的点与遗漏处,在下方会逐渐补充我们先来看一下一些被抽象的算法是如何实现的1. 视口变换先来看一下视口变换做的事情:将坐标原点从 [-1, 1] [-1, 1] 的 [0, 0] 原点移动到 [0, width] [0, height] 的[0, 0] 原点将坐标系的 X, Y 坐标( 宽高 )从 [-1, 1]^2 变换为 width & height简单来说就是将一个宽度与长度为 2 的矩形,通过缩放与位移的方式变换为一个宽高为 width & height 的矩形,通过矩阵的表示方式,即为接下来简单实现一下/**当前函数接受一个由向量表示的坐标,内部包含一个如上表示的4*4矩阵两者相乘,返回一个新的坐标向量向量:即一个1*4的矩阵[x,y,z,w],w的作用是为了辅助位移运算,如上图矩阵所示矩阵:在图形学当中多用4*4的矩阵a1a2a3a4b1b2b3b4c1c2c3c4d1d2d3d4*/functiongetViewPortPosition(vector){//width=canvas.width;height=canvas.height;constmatrix=newMatrix(width/2,0,0,width/2,0,-height/2,0,height/2,0,0,1,0,0,0,0,1);//向量与矩阵相乘,返回一个新的向量坐标returnvec4MulMat4(vector,matrix);}向量 * 矩阵示例 ---> 返回一个新的向量2. 通过叉乘判断当前坐标点是否位于三角形内部通过当前坐标( 向量 )与三角形的三个边( 向量 ) 分别进行叉乘计算,如果符号相同,证明与三个向量处于同侧,即该点位于三角形内部,下方会给出另一种更通用的判断方式,此处的计算方式简单了解原理即可2 3D 模型渲染至此我们对于如何渲染一个基础的三角形有了简单的了解,但其中依然饱含许多困惑,比如对于包含诸多三角形的一个模型,每渲染一个面,都需要重新完全遍历一次画布,显然是十分浪费的。如果观察仔细的话,会发现三角形边缘存在着非常明显的锯齿状,是什么导致的,如果进行优化我们能获取到顶点的 x y 坐标,进而在纹理图片当中查找对应的颜色,那么在一个面当中的那些像素坐标,如果确定其 x y 坐标如何将一个 3d 模型转换为一个可以渲染的 2d 图像,其中的透视与遮挡关系如何处理我们先来逐一对这些问题进行解决,然后再1. 盒包围模型: 通过确定一个三角形( 或多个三角形 )的最大与最小 X Y 值,来圈定一个更小的遍历范围constboundBox=getBoundBox(position1,position2,position3);for(letx=boundBox.minX;x
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 会员注册

本版积分规则

QQ|手机版|心飞设计-版权所有:微度网络信息技术服务中心 ( 鲁ICP备17032091号-12 )|网站地图

GMT+8, 2025-1-14 03:58 , Processed in 0.738167 second(s), 26 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表