zoukankan      html  css  js  c++  java
  • 《WebGL编程指南》学习笔记

    《WebGL编程指南》学习笔记

    第二章

    缓冲区:

    • 颜色缓冲区(gl.COLOR_BUFF_BIT): 存储绘画到屏幕上的像素点
    • 深度缓冲区(gl.DEPTH_BUFFER_BIT): 存储深度检测的当前深度值
    • 模板缓冲区(gl.STENCIL_BUFFER_BIT): 存储模板检测的当前对比值
    缓冲区 默认值 函数
    颜色缓冲区 (0.0, 0.0, 0.0, 0.0) gl.clearColor(r, g, b, a)
    深度缓冲区 1.0 gl.clearDepth(depth)
    模板缓冲区 0 gl.clearStencil(s)

    注意:gl.clear(缓冲区1 | 缓冲区2) 可以清除指定缓冲区,可以用'|'来指定多个缓冲区

    //用蓝色来清空颜色缓冲区
    gl.clearColor(0.0, 0.0, 1.0, 1.0);
    gl.clear(gl.COLOR_BUFFER_BIT);
    

    齐次坐标:

    图片

    百度百科

    博客


    第三章

    缓冲区对象:WebGL里的一块存储区域,可以在缓存区里保存所有要绘制的顶点数据,然后一次性向顶点着色器里传入多个点的attribute变量数据

    注意,向缓冲区写入数据,一定是在初始化顶点数据时执行,不要放在update里,因为这个操作很耗时,而且也没必要每帧都写入数据,只需要每次渲染

    的时候重新绑定缓冲区就行(通过创建多个缓冲区以牺牲内存的方式拯救CPU)

    矩阵变换原理:从三角函数推断各种变换矩阵


    第四章

    矩阵数学库

    单位矩阵的作用

    “变换”是一个动作概念,例如“将物体与位移变换矩阵相乘”,意思是将物体位移

    矩阵变换的顺序(先旋转后平移)

    先旋转/缩放后平移,跟先平移后旋转/缩放效果完全不同。
    例如一个物体在坐标原点(0,0),想要旋转90°跟平移5个距离
    先旋转后平移:
        1.将旋转矩阵乘以物体坐标,实现一个将物体绕原点旋转90°的矩阵
        2.将平移矩阵乘以上面的矩阵,将绕原点旋转90°的矩阵,平移5个单位
    先平移后旋转:
        1.将平移矩阵乘以物体坐标,将物体平移5个单位
        2.将旋转矩阵乘以以上矩阵,将物体平移5个单位后的位置再绕原点旋转90°
    

    必须严格按照先Scale,再Rotate,再Translate的顺序,否则得到的结果肯定是不满意的
    例如有一个1X1正方形在原点,我们想要得到一个1X2,并且斜向45°,而且离坐标原点1个单位处
    如果先旋转,再缩放的话,旋转方向是对了,但是我们是将旋转后45°的正方形的Y轴拉伸2倍,得到的是一个被拉长的菱形
    如果先平移,再旋转的话,因为我们旋转都是绕着坐标原点的,结果自然是正方形不是自身旋转45°,而是绕着原点旋转

    /*
     * 注:实际上大部分游戏引擎都会处理这一部分逻辑,例如cocos引擎,不管我们先设置旋转
     * 还是先设置平移,最终的结果都是一样的,因为不管你写哪个,引擎都不会立刻去乘矩阵,
     * 而是设置一个脏标记,然后在下一次循环的时候,按照先计算 scale -> rotate -> translate
     * 的顺序执行,之所以要按照这个顺序,是因为如果把平移放在前面,会出现与开发者预期不符的效果,例如开发者明明想通过代码设置一个在坐标(5,0)处,带有90°旋转的物体,由于书写顺序,导致变成一个在坐标(5,0)处绕原点旋转90°的物体
     */
    node.position = vec2(5, 0)
    node.rotate = 90 
    
    但是如果是自己计算矩阵,还是要注意顺序,不然最后的结果可能也会不一样
    

    平移,旋转,缩放矩阵的推断过程

    绕任意点旋转/缩放(只有2D模式下才有意义,3D模式不存在锚点的概念)

    可以通过转换为复合变换将旋转变为绕原点旋转:
        1.获取一个将旋转点平移到原点的矩阵,从该矩阵变换,实现目标点平移
        2.目标点根据旋转矩阵,绕原点旋转
        3.将旋转后的点,再平移回原来旋转点所在的矩阵
    

    https://www.cnblogs.com/zhoug2020/p/7842808.html

    绕任意轴旋转(3D模式下)

    1.将绕X轴,绕Y轴,绕Z轴的3个矩阵乘起来,合并成一个新的矩阵,就是绕任意轴
    即依次按照XYZ轴的旋转进行变换
    因为矩阵相乘先后顺序很重要,所以不同次序的旋转会得到不同的结果,通过这种方式获取旋转矩阵,除了要给出欧拉角,还需要给出旋转顺序
    https://threejs.org/docs/#api/en/math/Euler.order
    http://planning.cs.uiuc.edu/node102.html

    2.如果不想严格按照某个轴的顺序先后,那么应该将绕任意轴分解为3个轴的投影,然后分别旋转各自的轴
    https://blog.csdn.net/Master_Ding/article/details/82459342

    旋转矩阵,维基百科
    1

    一个网站,可以输入数据,生成旋转矩阵,四元数啥的

    从矩阵提取位移,旋转,缩放矩阵

    【第三弹】从矩阵中提取平移、旋转、缩放矩阵(有推算过程)
    Given this transformation matrix, how do I decompose it into translation, rotation and scale matrices?

    从旋转矩阵获取欧拉角

    从旋转矩阵获取欧拉角 DECOMPOSING AND COMPOSING A 3×3 ROTATION MATRIX
    https://stackoverflow.com/questions/15022630/how-to-calculate-the-angle-from-rotation-matrix
    http://www.gregslabaugh.net/publications/euler.pdf

    仿射变换

    转置矩阵

    逆矩阵,因为M*-M = I,而任何矩阵乘I又等于自身,也就是说,一个顶点经过矩阵M的变换后,Mv,可再

    乘M的矩阵来还原,即M-Mv = I*v = v

    法线通过逆转置矩阵变换后可以得出正确的垂直效果的推算过程

    四元数

    https://krasjet.github.io/quaternion/quaternion.pdf

    四元数的插值

    四元数右乘三维向量(得到将三维坐标进行该四元数旋转后的新坐标),是否是通过旋转矩阵变换得到的?


    第五章

    纹理的配置,纹理的缩放模式,纹理大于UV时如何显示,等等

    纹理的尺寸一定要是2的N次方,否则无法显示

    立方体纹理贴图

    利用mipmap金字塔纹理,解决由于图片缩小导致的闪烁,抖动锯齿

    利用gl.generateMipmap可以自动生成金字塔纹理,避免麻烦的手动创建以及加载,但会造成一定开销,如果为了性能考虑,还是要手动创建

    gl.FILTER_MINMAP_SELECTOR,SELECTOR表示如何选择mip层,是最近的,还是2个层直接插值,FILTER表示选择完mip层后,该层的纹理的过滤模式,结论:gl.LINEAR_MIPMAP_LINEAR效果最好,但比较吃性能,因为他是完全插值的,gl.NEAREST_MIPMAP_NEAREST则反之,它只选择最近的金字塔贴图

    参考:https://www.cnblogs.com/kekec/p/5065155.html

    各项异性过滤


    第七章

    摄影机空间变换

    观察空间矩阵的推导

    //方式1: 直接变换到摄影机的世界坐标,即变换到摄影机矩阵的逆矩阵里
    //方式2:构建一个先朝摄影机反方向移动,再反方向旋转的矩阵,其实得到的也就是上面摄影机的世界坐标矩阵
    //从哪里看向哪里,也可以理解为摄影机视角,即观察空间
    //若要变换到摄影机空间,可以假设整个观察空间以摄影机位于世界坐标原点,然后将所有物体朝摄影机原先在世界空间中的位置反向移动即可
    //在纸上画下图就清晰了

    投影矩阵的推导

    没有指定投影矩阵时,默认的矩阵是如何裁剪的(书里说是默认的矩阵深度很浅,所以三角形旋转会导致缺角看不见)

    !!!!!!!!!震惊,今天在做天空盒贴图时,为了让天空盒纹理永远显示在最远的地方,会将gl_Position的z轴设置为1,
    原来是因为webgl里的显示是一个2X2X2的矩形盒,屏幕左边为x=-1,下边为y=-1,最远的地方为z=1,最近的为z=-1
    任何最终gl_Position(p.x / w, p.y / w, p.z / w)值超出这个范围的,都不会被渲染,即被裁剪掉
    这时候齐次坐标的作用就来了,附加一个w值来换算深度,例如一个坐标在z=100处远的物体,假设w=1,即没有经行裁剪矩阵的情况,
    它的最终坐标z=z/w=100,肯定会被裁剪,假设此时的摄影机far距离刚好为100,进行裁剪矩阵变换后,算出w=100,
    gl系统再次获取这个坐标,就是z=z/w=1,刚好在最远的地方,不会被裁剪。相当于裁剪矩阵的作用,就是将摄影机视锥体的内容映射到
    这个2X2X2的立方体里
    这也是为什么gl_Posintion.w能用来表示物体离摄影机距离的关系,
    (暂时还搞不懂w表示距离是怎么算出来的)

    https://blog.csdn.net/stl112514/article/details/83927643
    https://zhuanlan.zhihu.com/p/50547909
    https://zhuanlan.zhihu.com/p/73034007

    投影矩阵裁剪的原理,为何乘以一个矩阵,就会有部分区域看不见了

    开启深度检测

    通过指定三角形索引来绘画代替直接用顶点数组绘画

    第八章

    平行光

    点光源

    第十章

    加载解析obj格式模型文件

    参考:https://en.wikipedia.org/wiki/Wavefront_.obj_file

    雾气

    顶点着色器的顶点经过MVP变换后,gl_Position.w能表示该顶点距离摄影机的距离,原理?

    渲染到纹理

    创建帧缓冲区
    //如果要渲染到纹理,那么渲染的屏幕大小就是纹理的大小,渲染纹理相当于屏幕
    //!!!!!注意,渲染纹理一定是要2的N次幂,否则是黑色的显示不了
    过程:
    1.创建一个帧缓冲对象
    2.创建一个和屏幕大小相同的纹理(2的N次方)
    3.将纹理附加到帧缓冲上
    4.渲染时,指定将其渲染到特定的帧缓冲上。不需要渲染到帧缓冲时,我们需要关闭这一效果。
    5.将纹理通过普通方式渲染出来

    alpah 混合 和深度写入

    //注意,由于某些操作会锁定深度写入,因此在gl.clear里即使有指明要清除深度信息,由于被锁定,也无法成功清除
    //因此如果gl.clear指明要清除深度信息,应该先确保下是否有解锁深度缓冲区的写入功能

    创建缓冲区过程

    创建着色器过程

    加载纹理流程

    切记一句话:矩阵变换,是指将一个点(或矩阵)按照该矩阵的描述进行运动,而不是之前老是错误的认为是将点转到该矩阵坐标系下

    例如对某点进行一个平移矩阵的变换(即左乘),意思是将这个点按照这个矩阵描述的进行平移

    所以要将某个点转到某个矩阵的局部坐标系下,是要乘该矩阵的逆矩阵,即反方向平移

    var mat = new Matrix4x4(
    new Vector4(xAxis),
    new Vector4(yAxis),
    new Vector4(zAxis),
    new Vector4(0, 0, 0, 1));

    竟然能直接通过这种神奇的方式构建旋转矩阵,
    也就是意味着,第一行代表的就是X轴的指向,
    改天算下,这种方式跟旋转矩阵的推导有啥关系

    重要:gl里的bind操作的意思

    gl里的每个操作,例如gl.texImage2D(gl.TEXTURE_2D),并没有在函数里指明你要操作的对象,即这个函数它不知要给哪个纹理设置,
    它只指定接下来要操作的类型是纹理,因此gl系统使用一个叫绑定的机制,即类似gl.bindTexture(gl.TEXTURE_2D, target),
    意思是将某个纹理对象绑定到当前的操作纹理的目标,接下来的所有操作就会都针对它进行
    因此在每次执行某些gl操作的时候,都要先注意操作的目标有没有先被绑定
    还有操作完了有些目标注意要解除操作,防止下次操作某个功能的时候,忘了绑定新对象,而被操作成旧的对象

    造成的误操作典型的问题就是,渲染2个对象,第一个纹理,绑定并绘画,第二个没纹理,跳过设置纹理步骤,但如果上一步没取消绑定,会让第二步

    误认为它有而渲染出来

    出于性能考虑你希望在远的物体之前先绘制近的物体,因为GPU使用深度缓冲检测,会不绘制无法通过测试的像素。
    也就是说,绘画普通物体,应该由近到远绘画,避免远处的物体像素花半天绘画后,还被抛弃掉。
    而对于半透明物体,则要反过来,需要由远到近绘画,因为每个像素的颜色都会保留下来用于计算叠加

    “一個 Draw Call,等於呼叫一次 DrawIndexedPrimitive (DX) or glDrawElements (OGL),等於一個 Batch”
    https://blog.csdn.net/hany3000/article/details/44033243

    渲染顺序:
    顶点着色器->片段着色器->深度测试->alpha测试->颜色缓冲区
    1.不透明物体(包括天空盒),从近到远绘制,因为近的物体会遮挡远的物体的部分,如果先渲染远的,再渲染近的,会造成浪费,被遮挡部分也渲染了,但后面深度检测的时候又抛弃掉(但实际渲染顺序,深度测试在片段着色器之后,也就是说,不管遮挡不遮挡,都会渲染像素,记得好像有个优化方法是可以将深度检测提前的?不知叫啥)
    2.再渲染半透明物体,顺序反过来,从远到近绘制,因为近的物体,会与远处物体(即背景色)进行叠加混合,所以需要先绘制远的,否则一旦绘制了最近的半透明物体,其后面的物体由于都无法通过深度检测,不管透明不透明,全部显示不出来
    3.渲染半透明物体的时候,应该分2步,先开启正面裁剪,然后绘制背面,在开启背面裁剪,绘制正面,叠加上去,否则造成的后果就是,正面三角形可能先绘制,然后绘制背面的时候,由于背面像素通不过深度检测,被抛弃,导致透明物体的里层部分丢失,实际情况应该是,我们可通过正面看进去,看到物体内部
    图片

  • 相关阅读:
    Linux/windows查看设置环境变量指令
    转载:windows查看进程相关指令
    Ubuntu开启SSHD服务
    Ubuntu root方式登录
    洛谷P1466 集合 Subset Sums
    洛谷P1726 上白泽慧音
    洛谷P1983 车站分级
    洛谷P2577 [ZJOI2005]午餐
    洛谷P1119 灾后重建
    P1169 [ZJOI2007]棋盘制作
  • 原文地址:https://www.cnblogs.com/jeason1997/p/15211272.html
Copyright © 2011-2022 走看看