图形学中易混淆的知识笔记

本文对比了WebGL与Canvas的特点,介绍了WebGL如何利用OpenGL进行2D和3D绘图,以及Canvas作为HTML5元素的应用。同时深入探讨了显示器刷新机制、纹理UV坐标计算、空间变换原理等内容。

webgl与canvas的区别

webgl:可以直接调用底层的opengl进行绘图,可以绘制2d或者3d
canvas:只是一个画布,上面放置了一个2d画笔,只能绘制2d图形,绘制的图形只是通过webgl转译光栅才显示出来,此外canvas也是html5的元素,我们可以通过它拿到webgl的画笔进行绘图

显示器刷新

显示器像个大爷一样,每个一定时间就会从帧缓冲的颜色缓冲中取数据来刷新页面的像素框,这个和游戏渲染逻辑没有半毛钱关系,游戏渲染逻辑只是负责去更新这个颜色缓冲,至于何时被渲染到屏幕,完全有显示器说了算,一般显示器的刷新频率为60帧,也就是说要1s中取颜色缓冲拿60次数据,你游戏渲染逻辑最好跟上这个节奏,假设你每一帧都要不一样渲染结果的话,否则慢了就会卡顿,哈哈,快了你也影响不到人家显示器刷新,吃力不讨好,如果你不去更新渲染缓冲,GPU是个状态机,你上一次的渲染结果就会一直保存在这个颜色缓冲中,显示器就会一直拿这个数据填充像素框,那你的界面看上去就会静止
opegl(更新)------------颜色缓冲
显示器(1s获取60次)-----------颜色缓冲

纹理UV

纹理中像素的纹理坐标是从它们的边缘计算的。像我们在纹理的文章中提到的纹理坐标从0到1。0是最左边缘的像素,1是最右边缘的像素。如果你有宽度为3像素的纹理,那就像这样
在这里插入图片描述
选取某个特定像素,那么公式是

 (x + .5) / width

上面的每个像素是

 (0 + .5) / 3  = 0.166
 (1 + .5) / 3 =  0.5
 (2 + .5) / 3 =  0.833

或者
在这里插入图片描述

裁切空间—》屏幕空间

WebGL中的颜色值范围从 0 到 1
WebGL裁剪空间的x轴范围:[-1,1] 从左到右
WebGL裁剪空间的y轴范围:[-1,1] 从下到上
WebGL裁剪空间的中心或者原点:屏幕的正中心
NDC的坐标范围是:【-1,1】
屏幕空间的x轴范围:[0,gl.canvas.width]
屏幕空间的y轴范围:[0,gl.canvas.height]
屏幕空间的原点:位于屏幕的左下方
举个例子:假如我们的屏幕空间的大小是400 x 300,那么转换关系如下:

 裁剪空间            屏幕空间
 0, 0        ->     200, 150
 0, 0.5      ->     200, 225
 0.7, 0      ->     340, 150

屏幕映射:就是裁切空间的NDC坐标映射到屏幕空间的坐标

gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);

归一化
就是将数组的每一个元素大小统一缩放到[-1,1]

/**
     * Normalize a vec3
     * 归一化一个数组
     * @param {vec3} out the receiving vector
     * @param {vec3} a vector to normalize
     * @returns {vec3} out
     */

    function normalize(out, a) {
        var x = a[0];
        var y = a[1];
        var z = a[2];
        var len = x * x + y * y + z * z;

        if (len > 0) {
            //TODO: evaluate use of glm_invsqrt here?
            len = 1 / Math.sqrt(len);
        }

        out[0] = a[0] * len;
        out[1] = a[1] * len;
        out[2] = a[2] * len;
        return out;
    }

空间变换

一个空间坐标系用一个思维矩阵就可以表示,前三列分别表示x轴,y轴,z轴,这可以满足旋转和缩放,第四列表示坐标平移变换,一个原始的空间坐标系,是一个单位矩阵,即
x y z w
1 0 0 0
0 1 0 0
0 0 1 0
0 0 0 1
一个点【x,y,z,0】与这个矩阵相乘我们得到的新的点,这个新的点就位于这个空间坐标系下
所有显示节点都是有一个节点坐标系,如果两个节点之间有继承关系,那么就是两个空间坐标系相乘
举个例子:场景节点,普通节点,我们将普通节点放在场景节点上,假设场景节点就是世界坐标系,这个坐标系没有发生任何的改变,那么渲染的时候,我们需要把普通节点上的顶点变换到场景节点空间坐标系下
世界空间坐标系的位置和大小,旋转是不允许发生任何改变的,它是一个参照物,GPU渲染依靠它
所有节点都包含一个空间坐标系
摄像机也是一个节点,他其实控制了渲染的视野,我们经常说,一个顶点进入顶点着色器以后,需要完成空间变换,其实就是将顶点左乘以世界坐标系,将顶点转换到世界坐标系下,再左乘以视野坐标系,将顶点转换到相机坐标系下,再乘以投影矩阵,将顶点转换到齐次裁切空间坐标系下,当然并没有真正的完成投影,还需要进行一次齐次除法

节点坐标系里有各种各样的值,但变换只有三种,旋转,缩放,平移
两个矩阵相乘是看不出来什么意义的,比如模型节点坐标系左乘以场景节点,无非就是两个4x4的矩阵相乘,但如果我们将一个点和这两个坐标系连续左乘将发生几何意义,即我们把模型中的点转换到场景中,接着又左乘场景的节点坐标系,那就是把这个点转换到场景的父节点中,如果这个场景的父亲没有父亲,那就是转换到世界空间坐标下了

传统阴影的实现原理

在这里插入图片描述

摄像机控制着渲染视角,其实就是要将所有的渲染元素的位置转换到摄像机的节点坐标系下,我们首先将摄像机的位置移到光的位置,即光的节点坐标系和摄像机的节点坐标系完全一样,给他一个很简单的shader,将结果渲染到一张深度纹理中,其实光能够渲染的就是光能够看到的最近深度,看不见的地方那就是阴影了,接着开始第二次常规渲染,有一个公用的信息,就是模型在世界空间坐标系下的位置是一定的,所以我们使用这个顶点左乘以光的节点逆矩阵再左乘光的投影的矩阵,就可以得出来上一次渲染结果【x,y,z】,此时还有进行齐次除法,还需进行齐次除法,
上一次渲染中,将场景中所有的顶点进行了转换,将符合条件的像素的深度信息写入了深度纹理中,也就是说可能一个像素的位置,可能有多个顶点参与过测试,在深度纹理中的那才是可以渲染的,也就是光可以看见的。在第二次渲染的时候又跑去计算所有的顶点,算出每个顶点在深度纹理中的uv,取出光能够看到的最近的深度值,进行比较,如果该顶点的深度值大于纹理采样的深度值,根据近处优先渲染的原则,那么就看不到,则处于阴影中

第一次的渲染,就是将可见的最近的深度值保存在深度纹理中
在第二次的渲染中,其实又去计算了所有顶点在第一次渲染的空间变换,将结果和保存在深度纹理中的值进行比对

颜色相乘与相加

相乘:表示两种颜色相互叠加,颜色强度会减小
相加:趋向1靠近,增量
ambient + diffuse + specular

将图片打包成图集,降低drawcall的原理

图片合并,减少纹理状态切换,GPU是一个状态机,减少状态使用,是非常重要的!做到一次发送多次使用,这是原则
我们渲染一个显示节点,就是给GPU发送一堆顶点,然后针对这些顶点,又会有与之对应的一些顶点相关联的数据,比如uv,在光栅化的时候,会对uv进行插值,算出片元颜色,而我们在进行图集打包的时候,可以明确显示节点对应的图片的具体位置信息,通过这个信息,我们可以算出uv,最后可以准确渲染
既然图片合并了,那么对于那些不同的显示节点的顶点自然也是可以合并的,所以最终可以做到一次发送,多次渲染

gl.getAttribLocation与gl.getUniformLocation有何不同?

这两个函数都是获取着色器相关变量的地址
gl.getAttribLocation这个函数获取的是顶点相关变量的地址
gl.getUniformLocation这个函数获取的是uniform相关变量的地址
所有顶点相关的变量,他们的值都是和顶点位置一一对应的,而uniform变量是针对所有顶点,这一点非常重要!
顶点相关的变量有:位置,法线,切线,uv,颜色,后面四个变量和位置一一对应
uniform相关的变量有:节点颜色,各种矩阵

冷知识

1:只有旋转和平移,空间矩阵的逆矩阵就是它的转置矩阵
2:在计算光照的时候,需要重定向法向量,因为三维场景或者模型他们经常都是要发生变换的,那么法向量也要跟着变,这个时候不能乘以世界矩阵,因为如果是缩放变化,得到的法线就有问题,应该乘以世界矩阵的逆矩阵的转置矩阵
3:gl_FragCoord,片段着色器的输入变量,持有该片段的屏幕坐标及Z值,屏幕相对坐标(x, y, z, 1/w),假如,我们设定的viewport的渲染区域为(0, 0, 1280, 574) 这么大,那么,gl_Fragment的x分量 范围就在0~1280之间, y分量就在0~574之间

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值