计算机图形学入门OpenGL
计算机图形学openGL
计算机图形学:(Computer Graphics, 简称CG)
简单得说:计算机图形学是一种使用数学算法将二维或者三维图形转化为计算机显示器所能显示的二维栅格形式的科学。
Modeling(建模):构造场景的三维模型。
Rendering(渲染):将三维场景绘制在屏幕上。3D model -》2D image的
- 逐像素绘制;
- 逐面片绘制:
- 模型由许多面片(polygon)组成,速度更快,使用更多
- 逐面片得进行投影、光栅化(Rasterization)
Animation(动画):简单理解就是让动画动起来的技术
- 关键帧技术
- 过程动画记住
- 基于物理的动画技术
- 关节动画
- morphing和deformation(空间变形)技术
图形(Graph)与图像(Image)
- 图像:一堆像素组成的二维栅格
- 图形:含有表示"形"的几何信息
- 关系:图形之中也会有图像的成分
- 图形的显示最终也要转化为图像
- 图形中的纹理也就是图像
几个学科的区别
- 计算机图形学(Computer Graphics)
- CG:三维模型-》绘制-》二维图像
- 数字图像处理(Digital Image Processing)
- IP:二维图像-》分析处理-》得到其他的信息或者生成某种效果
- 计算机视觉(Computer Vision)
- 二维图像-》分析处理-》三维信息
图形流水线(Graphics Pipeline )
简单理解:在计算机将3d模型转化为屏幕上 的图像需要经过一系列的步骤处理,这个处理步骤就是图形流水线。
图形流水线总览:
模型处理阶段(在CPU上进行)-》(之后都在GPU上进行)顶点处理(Vertex operation) -》光栅化(Rasterization)-》片元处理(fragment operation) -》帧缓冲(frame buffer)-》屏幕上的二维图像
其中:
- 顶点处理包括:三维模型-》经过**变换(几何变换,投影,裁剪,视口变换)**转换成二维图形,还有光照计算!!!(为了效率)
- 光栅化阶段包括双线性插值计算,扫描线转换,法向插值。
- 片元处理包括:纹理映射,雾化,各种测试,混合等等
- CPU负责组织数据,负责把组成这个物体的模型片面的顶点数据组织好 ,然后CPU就不管了。
- GPU负责图形流水线(Graphics Pipeline )
OpenGL:Open Graphics Library(开发式图形编程库)
调用图形硬件的程序接口。
应用程序《--------》openGL《--------》调用图形显卡驱动
由大约150个函数组成,用户可以调用来完成各种绘制任务。
GPU:Graphics Processing Unit,
- GPU是显卡中的核心心脏
- GPU是用来处理屏幕显示相关的计算,并实现图形流水线
CPU:Center processing Unit
OpenGL Library(核心库)
- 包括115个函数,前缀:“gl”
- 例:glColor3f()
- gl.h
OpenGL utility library(实用程序库)
- 包括43个函数,前缀:“glu”
- 例:gluPerspective()
- glu.h
辅助OpenGl编程工具库
C\C++编程的基本流程
- 源程序编写-》预处理-》(.c文件.h文件和.cpp文件,统称文本文件)编译(Compile)-》(.obj文件,也叫二进制文件)链接(Link)-》(.exe文件.dll文件,也叫二进制文件)运行(exe)
- 调试Debug无处不在
- 出错分三种
- 编译错误
- 链接错误
- 运行时错误
OpenGL函数的基本语法:
OpenGL函数的命名规则
- 前缀:该函数属于哪个函数库
- 后缀:参数的维数,参数的数据类型,是否以数组的方式传递参数。
OpenGL的数据类型
注意:尽量使用GL内置的类型,为了跨平台。使用这个是有原因的
回调函数
不由用户来调用,由系统响应消息来调用的函数。需要一个注册函数,opengl里面常用一些回调函数来响应一些动作。
OpenGL的变换(Transformation)
总览:
几何变换-》投影变换-》裁剪-》视口变换
- 三维几何变换:
- glTranslate()
- glRotate()
- glScale()
投影
- glFrustum():视锥
- gluPerspective()
- glOrtho()
窗口裁剪
视口变换
- 一类
- glFrustum()
- gluPerspective()
- glOrtho()
- 二类
- glViewPort()
- 几何变换
glTranslate*()
glScale*()
需要注意的是:三角形并没有在原地放缩,而是离得更远了
是以原点(0, 0, 0)为中心的方缩放, 如果想以点p(x1,y1, z1) 为中心放缩的话,需要使用组合的方式实现(三个步骤):
Translate(-x1,-y1, -z1)
Scaling with(Sx, Sy,Sz)
Translate(x1,y1, z1)
glRotate*()
如果想实现沿着任意向量的旋转需要组合:
- 构建一个新的坐标系统O’xyz,该坐标系统以向量(Ax,Ay,Az)为z轴(右手 )
- 将顶点从Oxyz变换到O’xyz中
- 在坐标系统O’xyz,绕z轴旋转顶点
- 将旋转后的顶点从O’xyz变换回Oxyz中
A是一个正交矩阵,A的转置和A的逆是一样的。
平移,旋转,缩放,都是矩阵。
为什么要把各种变换表示成矩阵运算?
- 图形硬件特别喜欢计算这种向量和矩阵,本身硬件的设计路线适合于这类运算的实现。
- 表达成矩阵运算形式,可以把各种变换组合起来,这种组合一方面很简洁,另一方面,有可能节省运算量(比如说,连续变换的矩阵可以组合成一个矩阵)
需要注意的是这个T1,和T1是乘在矩阵M的右边,矩阵相乘的顺序是先平移再旋转,但是实际上却是相反的。
在OpenGl里面,其实是用堆栈来管理矩阵。
glPushMatrix()起到了保护环境的作用,glPopMatrix起到了恢复环境的作用;两者一般配合其来使用。
模型变换与视点变换
Model-View Transformation
Model Transformation:视点不变,变物体。
View Transformation:物体不变,变视点。
模型变换和视点变换是可以统一的。目前使用的变换可以认为视点总是不变的,所有的变换都是模型变换。
如果要使用视点变换的话可以使用gluLookAt()函数实现,gluLookAt()函数视图模拟一种物体不动,变换视点的控制效果。但是,本质上还是模型变换。
全局变换和局部变换
如果只有有一次变换(旋转,平移,缩放),那么全局变换和局部变换效果一样。
但是如果是多个变换的组合,那么它们一般效果不同。
局部变量正好相当于组合顺序相反的全局变换。
OpenGl中如果是单独来看是局部变换,但是多个变换是局部变换(也就是全局变换的想法)
OpenGl中采用的是列矩阵,所以其新矩阵总是乘在原矩阵的左边(列列矩阵相当于列矩阵的转置)。
投影变换(projection)
已知给定视点,实现方向,计算出当前顶点的投影点坐标。
透视投影(Perspective projection):有近大远小的特点
- 需要注意的是:near和far必须是正值,不能是负数,相当于他们的绝对值(far > near)。
- fovy:张角视角,aspect:比例
平行投影(Parallel projection):没有近大远小的特点
正投影(Orthographic projection):其实是平行投影的一种特殊形式,投影方向与投影面垂直,没有近大远小的特点。
glOrtho和glFrustum定义的投影空间可以是不对称的。但是glPerspective一定是和z轴完全对称的。
投影变换也都是用矩阵来实现的
有一个专门的堆栈来管理投影矩阵
glMatrixMode(GL_PROJECTION)
视口变换
- 投影函数定义了窗口的大小比例
- glViewPort定义了视口的大小比例
- 视口坐标系原点在左下角;投影窗口的原点在中心。
- 为了使得图像不变形,那么需要保证视口和窗口的宽高比是一致的。
三维几何空间中的坐标系与单位
- 右手坐标系
- 默认视点在原点
- 视线方向是z轴负方向
- 视椎体空间中的单位?在OpenGl的三维空间中,无绝对单位,只有相对的大小。
光照
如何计算光照明效果?
光照明模型(Illumination Model)
如果学到后面,会发现其实光照明模型就是BRDF材质的应用
方案:分而治之,将环境光 ,镜面高光,漫反射光分别求之,然后求和。
Phong Illumination Model
环境光 Ambient light (最难模拟),与视点位无关
环境光非常复杂,但是Phong模型中只采用一个常数来表示
- Ie = kaIa
- Ia: 环境光的亮度
- ka:物体表面的环境光反射系数,0 <= ka <= 1
漫反射光 Diffuse reflection ,与视点位无关
粗糙表面的光会向四周均匀反射
镜面反射光(高光) Specular reflection,与视点位有关
Phong Illumination Model
R = 2N(N.L) - L,计算量比较大,实际使用中,由于R计算不方便,因此常用(N.H)代替(R.V);H为L和V的角平分线上的单位向量。
H = normalize(L+V),normalize:单位化
Blinn-Phong Model
OpenGL中光照的参数设置
step1.设置好物体的法向量。glNormal3f(Nx,Ny, Nz)
I = KaIa + KdIl(N.L) + KsIl(N.H)^n
step2.打开光照:
glEnable(GL_LIGHTING)
glEnable(GL_LIGHT0),最多8栈灯从light0-light7.如果超过8栈灯,可以使用shader来实现。
Step3.光照参数
glLightfv(GL_LIGHT0, GL_AMBIENT, vLitAmbient)
glLightfv(GL_LIGHT0, GL_DIFFUSE, vLitDiffuse)
glLightfv(GL_LIGHT0, GL_SOECULAR, vLitSpecular)
glLightfv(GL_LIGHT0, GL_POSITION, vLItPosition),表示光源的位置
Step4.设置材质参数
glMaterialfv(GL_FRONT, GL_AMBIENT, vMatAmb) ,Ka
glMaterialfv(GL_FRONT, GL_DIFFUSE, vMatDif),Kd
glMaterialfv(GL_FRONT, GL_SPECULAR, vMatSpec),Ks
glMaterialfv(GL_FRONT, GL_SHININESS, vShininess),n
glMaterialfv(GL_FRONT, GL_EISSION, vEmission),自发光
Step5,设置聚光灯参数
glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, vSpotDir),聚光灯的方向
glLightfv(GL_LIGHT0, GL_SPOT_CUTOFF, vLitCutoff),角度,哪个范围内有光。
glLightfv(GL_LIGHT0, GL_SPOT_EXPONENT, vSpotExponent),聚光灯的衰减,偏离聚光灯方向的时候回衰减
衰减相关参数
glLightfv(GL_LIGHT0, GL_CONSTANT_ATTENUATION, kc),常数衰减
glLightfv(GL_LIGHT0, GL_LINEAR_ATTENUATION, kl),线性衰减
glLightfv(GL_LIGHT0, GL_QUADRATIC_ATTENUATION, kq), 二次方的衰减
衰减因子 = 1/ (kc + kl d + kqd^2) ,d表示光到点的距离,d越大,光效越暗。
OpenGL的状态机的特性,所有的OpenGL的状态参数都有一个默认值。
此处Kc的默认值是1, Kl和Kq的默认参数都是0
几种光照模式
方向性光源,位置性光源
glFloat vLitPosition[] = {1.0, 1.0, 1.0, 0.0}
glLightfv(GL_LIGHT0, GL_POSITION, vLitPosition)
若vLitPosition的w值为0.0, 则为方向性光源,否则为位置性光源。
本地视点无限远视点
glLightModelf(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE);
—本地视点/无限远视点。
双面光照
glLightModelf(GL_LIGHT_MODEL_TWO_SIDE, 0.0).
讨论两个问题
1)如何让光源运动?
1.直接变化vLitPosition中的值
glLightfv(GL_LIGHT0, GL_POSITION, vLitPosition)
2.光源可以认为是一个几何物体,将受到其前面的几何变换的影响
2)光照下物体的颜色有什么来决定?
1.光的颜色:glLightfv()
2.物体的材质:glMaterialfv()
有一个点,如果OpenGL一旦打了光,那么glColor就失效了,但是可以通过以下方式发生作用。
glEnable(GL_COLOR_MATERIAL)
glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT)
通过这句话,让color的值变成ambient的值。
光照计算发生在流水线中的哪个阶段?
Vertex operations,只会在顶点计算光照,效率更好!。在光栅化阶段,会根据顶点的颜色来计算中间的颜色。
计算三角形内每个颜色的过程:(Shading:明暗处理,着色)
OpenGL中的Shading:
- Flat shading:常数明暗处理
- smooth shading :Gouraud 明暗处理
有几种常见的处理方法:
常数明暗处理(Flag shading)
面片内所有像素都取同样的颜色,是一种顶点级光照计算
Gouraud 明暗处理 (Gouraud shading)
双线性插值计算面片内像素的颜色, 是一种顶点级光照计算
计算出多边形各个顶点的法向量(对周围面片法向进行平均求得次顶点的法向)(重要)(CPU)
计算出多边形各个顶点的光亮度值。(vertex operation)
对多边形顶点的光亮度值进行"双线性插值"计算出多边形任意片元的光亮度值。(Rasterization)
二次插值,所以叫双线性插值。
双线性插值发生在光栅化阶段。
在光栅化阶段效率非常高!
Gouraud 明暗处理的问题:
- 曲面分割过粗可能产生错误效果
- 高光的丢失
- 马赫带效应(即光亮度变化率不连续的边界呈现亮带或者黑带)
Phong 明暗处理(Phong shading)
不差之光亮度的颜色,而是插值法向量。是一种像素级光照计算,也称为"法向量插值明暗处理"
- Step1.计算出多边形各个顶点的法向量(CPU)
- Step2.对多边形中的某个片元,双线性插值计算其法向量(Rasterization)
- Step3.计算出多边形内该片元的光亮度(fragment operation)
Phong 明暗处理缺点:计算量大, 以往图形硬件中不长采用。
图形流水线三个阶段的进一步理解
顶点处理-光栅化-片元处理
vertex operation处理的对象是顶点,fragment operation处理的是片元。
光栅化做了什么?
以三角形面片作为处理单位,输入的是三个顶点的数据,输出的是三角形所覆盖的所有的像素数据。
光栅化模块没有直接的接口函数,只有间接的函数。
glShaderMode(GL_FLAT/GL_SMOOTH);
glPolygonMode(GLenum face, GLenum mode);
设置画物体是用线框还是用面来处理,影响图元装配。目前还无法对光栅化模块进行shading编程。
帧缓冲区与片元
缓冲区的每一个单元对应的就是屏幕上的一个个像素
对片元的处理就是利用并修改缓冲区的数据,可以想象,一块缓冲区就是一块内存空间。它不是放在内存上,它是放在显存上。
显示器的分类
随机扫描显示器
光栅扫描显示器
像素的颜色都要存储在一块缓冲区中
需要不断重复绘制,即不断刷新屏幕(很快,一秒扫描60屏幕,一屏也就是一帧)
与视口的像素意义对应
单位:FPS:frame per second
几个种类的帧缓冲区
颜色缓冲(Color buffer)
color buffer记录每个像素的颜色。
存储视口中每个像素的信息,颜色缓冲区放在显存上,也就是放在显卡那个存储空间上。
假设:Resolution:分辨率为:1024 * 768
像素颜色:采用RGB三个分量来表示颜色,每个分量采用8bits,相当于一个字节来表示。
那么该颜色缓冲区需要的存储空间是:1024 * 768 *3byte =2.25M
glClearColor(0.0, 0.0, 0.0, 0.0)
glClear(GL_COLOR_BUFFER_BIT)
glClearDepth(1.0)
glClear(GL_DEPTH_BUFFER_BIT)
以上都是给图形流水线传达一个参数
gl推荐同时清除帧缓冲和深度缓冲,效率更高
glClearColor(0.0, 0.0, 0.0, 0.0)
glClearDepth(1.0)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT )
采用按位或。
单缓冲,双缓冲
双缓冲可以避免屏幕闪烁,理由是在画的过程中是一行一行画的,单缓冲屏幕可能会出现画的过程。出现屏幕闪烁,效果不好,而双缓冲没有画的过程。只有全图。但是需要注意的是,如今的操作系统对缓冲做了一个优化,及时外部设置单缓存,起内部也是会使用双缓存的。
front buffer
back buffer
深度缓冲(Depth buffer)
depth buffer记录每一个像素的深度。我们经常把depth-buffer叫做z-buffer。其最常用到的一个应用是消隐(visible suface detection)。来判断哪个面在前面,哪个面在后面。
1.在3d到2d的变换中,其实有一个z值,记录深度。
- (x, y, z)->(wx, wy)其投影后也有一个z值,当为(wx, wy,wz)
- wz介于0到1之间,被归一化了
- [外链图片转存失败(img-hMgKFCLl-1564487295330)(/Users/william/Library/Application Support/typora-user-images/image-20190626122802719.png)]
- 可以看出,其深度分布不均匀,距离视点越近密度越大,精度越高。
2.在投影时,每个投影点的深度(也就是wz)也会被记录下来,写入深度缓冲。
3.其实是:光栅化时候,每个片元的深度会计算出来,写入深度缓冲。深度缓冲区记录的是片元的深度(也就是wz),而不是顶点的深度(也就是z)
消隐的方法:
Depth Sorting Method(深度排序算法)
Area-Subdivision Method(区域子分算法)
Ray Casting Mathod(光线投射法)
**Z-buffer算法:**最常见
- Z-buffer算法在像素级上以近物取代远物,面片在屏幕上的出现顺序是无关紧要的。
- 这种取代方法实现起来比总体排序灵活简单,有利于硬件实现。(硬化到流水线)
- 缺点:占用空间大,没有利用图形的相关性与连续性。
glEnable(GL_DEPTH_TEST):两个作用
- 会进行深度比较
- 会根据当前绘制的内容更新depth buffer
void glDepthFunc(GLenum func)
- 深度比较的方式:Func
- func可取:GL_NEVER,GL_LESS,GL_LEQUAL, GL_LEQUAL, GL_GREATER, GL_NOTEQUAL, GL_GEQUAL, GL_ALWAYS
void glDepthMask(GLboolean flag)
- 设置是否更新深度缓冲
- 如果不想更新深度,将flag设置为false
glDisable(GL_DEPTH_TEST);
- 深度比较和更新深度缓冲这两个动作都不会发生
如果不想更新深度缓冲,只想比较
glEnable(GL_DEPTH_TEST)
glDepthMask(GL_FALSE)
如果不想比较深度值,只想更新深度缓冲,那么可以
glEnable(GL_DEPTH_TEST)
glDepthFunc(GL_ALWAYS)
glDepthMask(true)
默认**是
glDisable(GL_DEPTH_TEST)
glDepthMask(GL_FALSE)
glDepthFunc(GL_LESS)
模板缓冲(Stencil buffer)
相当于起了一个过滤的作用。效率很高。
glEnable(GL_STENCIL_TEST)
- 进行模板检测
- 更新模板缓冲
void glStencilFunc(GLenum func, GLint ref, GLuint mask);
- 模板比较的方式
- 谁在和谁比较?ref的值和当前屏幕上的像素所对应的模板缓冲中的值进行比较。
- 模板缓冲中的值是怎么来的?需要你在第一遍绘制的时候画入模板缓冲。相当于画了两个场景。(多遍绘制Multi-pass?第一遍为第二遍准备数据)
- mask的作用?在比较前,ref和模板值都会与mask进行and操作(按位与),也就是说这个mask值可以控制ref和模板的哪一位进行比较操作。
- func可取:GL_NEVER,GL_LESS,GL_LEQUAL, GL_LEQUAL, GL_GREATER, GL_NOTEQUAL, GL_GEQUAL, GL_ALWAYS
void glStencilOp(GLenum fail, GLenum zfail, GLenum zpass)
- 设定如何更新模板缓冲
- fai:片元没有通过模板测试时所执行的操作
- zfail:片元通过了模板测试,但没有通过深度测试时所执行的操作
- zpass:片元通过了模板测试,也通过深度测试时所执行的操作,或者没有执行深度测试时所执行的操作。
- GL_KEEP,GL_ZERO, GL_REPLACE,GL_INCE,GL_DECR,GL_INVERT
- GL_REPLACE:用glStencilFunc()函数中所指定的参考值替换模板的参数值。
如何绘制模板缓冲呢?
- glClearStencil(0x0)
- glClear(GL_ALWAYS_BUFFER_BIT)
- glStencilFunc(GL_ALWAYS, 0x1, 0x1)
- glStencilOP(GL_REPLACE, GL_REPLACE, GL_REPLACE )
累积缓冲(Accumulation buffer)—已经废除
用户自定义的缓冲
片元(fragment)与像素的区别?
小方格-》片元,一旦画到缓冲区之后-》像素,区别就在于,像素最终的颜色可能来自多个片元。
片元操作
片元在写入帧缓冲之前要按书按需经历一系列的测试,在写入帧缓冲之时也会经历一些运算。
操作顺序如下
纹理贴图
主颜色和辅助颜色汇合(区分光照)
雾效计算
- 裁剪测试
- alpha测试
- 模板测试
- 深度测试
- 混合
- 抖动
- 逻辑操作
其中,前面四个是在测试阶段,也就是写入帧缓冲之前,后面三个是,也就是写入帧缓冲之时。
如果片元在其中某个测试中被排除,那么后面的测试或者操作就不进行了。
裁剪测试
void glScissor(GLint x, GLint y,GLsize width,GLsize height)
定义了一个区域,x,y的区域的左下角。
glEnable(GL_SCISSOR_TEST)
只在矩形框里面的场景会被画,之外的场景不会被画,效率非常高,如果要矩形窗口裁剪,glScissor的效率会更高。
Alpha测试
- glEnable(GL_ALPHA_TEST)
- void glAlpahFunc(GLenum func,GLclampf ref)
- func包括GL_KEEP,GL_ZERO, GL_REPLACE,GL_INCE,GL_DECR,GL_INVERT
- Alpha测试比较像素片元的alpha值,与ref进行比较。
- 那么片元的alpha值是哪里来的呢?
- alpha片元测试在OpenGL3.0中被废弃了,并且在OpenGL3.1中代替为通过使用discard操作在 fragment shader中丢弃片元。完全可以用编程实现。
混合(blending)
将当前片元(正要写入颜色缓冲)的颜色与已经在颜色缓冲中的值进行混合。
常用方法:加权平均
Color = alpha * color-s + (1- alpha)* color-d
这仅仅是一种混合方式。
- 当前片元的颜色:源颜色Cs(Rs, Gs, Bs, As):source
- 已经在颜色缓冲中的颜色:模板颜色Cd(Rd, Gd, Bd, Ad):destination
- 源片元的混合因子:S(Sr, Sg, Sb, Sa):权重
- 模板片元的混合因子:D(Dr, Dg, Db, Da)
- 最终的混合颜色 = Cs * S + Cd * D
混合相关的函数
glEnable(GL_BLEND)
void glBlendFunc(GLenum sfactor, GLenum dfactor),混合的方式。
Cs * S + Cd * D
void glBlendEquation(GLenum mode),混合方程。
例子,半透明混合的因子设置。
void glBlendFunc(GLenum sfactor, GLenum dfactor)
Color = alpha * color-s + (1 - alpha) *color-d
sfactor 取GL_SRC_ALPHA
dfactor取GL_ONE_MINUS_SRC_ALPHA
注意:画半透明物体的时候,必须保持从远到近的顺序,也就是说必须排序(针对视点)。
深度检测没通过。
如果场景中物体都是不透明的,那么如下三种绘制方法哪种效率最高?
- 物体按照从远到近的顺序进行绘制。
- 物体按照从近到远的顺序进行绘制。
- 物体之间不排序,随便绘制。
答:如果忽略掉排序的时间,第二种效率更高,因为z-buffer检测会让你少画很多东西,在测试阶段会少掉很多东西。
多边形偏移
OpenGL中可以设置物体的点,线,面绘制模型。
1.void glPolygonMode(GLenum face,GLenum mode)
2.face:GL_FRONT,GL_BACK,GL_FRONT_AND_BACK
3.mode:GL_POINT,GL_LINE,GL_FULL
有的时候我们需要同时绘制两种模式,如在GL_FILL显示表面时同事显示line以突出多边形的边。
多边形偏移功能
- 1.为片元的z值增加一个适当的偏移值,把重合的z值适当得分开一些,使着重显示的直线与多边形的边缘清晰的分离开来。
- glEnable(GL_POLYGON_OFFSET)
- void glPolygonOffset(GLflaot factor,GLfloat units)
- 偏移值offset = m * factor + r * units,其中,m和r是光栅化阶段自动会算出来的值
- m是多边形的最大深度倾斜率(是光栅化阶段自动会算出来的值)
- r是保证能够产生可解析区别窗口坐标深度的最小值,r是一个因OpenGL实现而异的常数。
雾效(fog)
雾效是采用混合计算来实现的
Color = f * Color-s + (1-f)*Color-f
f是加权混合因子,有三种计算方式
GL_LINEAR, f = (end - z)/(end - start),start是雾开始的地方,end是雾结束的地方。z是雾的深度值。
GL_EXP
GL_EXP2
衰减的更快
与fog相关的函数
glEnable(GL_FOG),打开雾效
glFog*()用来设置与雾相关的参数
glFogCoord*()指定每个顶点的雾坐标
雾坐标决定雾的浓淡,深浅。
片元的颜色来自于光栅化,片元的在光栅化的基础上进行检查,处理,运算。
纹理的概念
如何实现表面细节的效果?
- 细化模型表面的集合
- 采用纹理映射的方法
- 颜色纹理
- 二维纹理
- 三维纹理
- 几何纹理
- 凹凸纹理映射(Bump mapping)
- 位移纹理映射(Displacement mapping)
- 颜色纹理
Texture mapping(纹理映射, 纹理贴图)
纹理映射的主要思想?
给一给定的**纹理函数(图就是二维的纹理函数)**映射到物体表面上,在对物体表面进行光亮度计算时可采用相应纹理函数值来影响光照模型中的参数(入漫反射光亮度)以产生纹理效果。
注意
纹理不仅仅可以改变颜色信息,还可以改变所有的信息。
三维纹理相当耗费空间。
凹凸纹理映射
基本思想:
- 用纹理去修改物体的法相而不是颜色
- 物体表面的集合法向量保持不变,我们仅仅改变光照明模型计算中的法向。
Bump map的三种记录方法
- offset map
- height-field map
- Normal map
Relief Mapping(浮雕贴图)
纹理信息 + 合适的shader -》产生效果。
计算过于复杂,不利于实现
位移纹理贴图
绘制物体时,对物体表面采样,
代价确实是大。
纹理映射关系的确定
- 投影式纹理映射
- 两步法纹理映射
- 中间几何体-》最终
- 展uv:
- 剖开,分块,也就是我们平时用的3d
- 过程式纹理
纹理映射在图形流水线中的实现
纹理坐标:顶点在空间中对应的坐标;
纹理映射是从顶点阶段开始的。
纹理映射在流水线中的实现过程:
- 1.在顶点处理阶段,给定顶点的纹理坐标
- 2.在光栅化阶段:插值得到每个片元的纹理坐标
- 3.在片元处理阶段,根据纹理坐标获取片元的每一个纹理值
OpenGL中使用纹理映射
应用纹理的四个步骤:
- 1.创建纹理对象,并为它装载一个纹理
- 2.确定纹理如何应用到每一个像素上
- 3.启动纹理贴图功能
- 4.绘制场景,提供纹理坐标和几何图形坐标
纹元(texel):纹理当中的单元。
顶点数组
为了画一个三角形调用了多个函数,导致太慢了,慢在函数调用(代价很大,消耗时间),调用函数之前,先为函数开辟一个栈空间,把每一个参数都压栈,入栈,然后才开始执行函数,执行完还得把每一个参数出栈,释放空间。
方法:减少调用函数的次数。
Vertex Array,一次性传入显存,固定流水线。
将顶点颜色,坐标,法向等一次性传入显存, 加快了速度
步骤:
激活(启用)顶点数组,最多可达8个数组
把数据放入数组,并且建立联系:glVertexPointer, glColorPointer, glNormalPointer
访问数组数据,绘制物体
方式1:访问单独是数组元素
glArrayElement(GLint index)
方式2:按照顺序访问整个数组元素
glDrawArray() (VAO)这一步会将所有的顶点数据一次性传入显存,起到了加速的作用。
方式3:创建一个数组元素的索引列表进行访问。
glDrawElements() (EBO)更灵活,避免重复放入数据。
对stride的理解:
步长:数据放在数组里面,顶点和顶点之间默认的空隙。
为什么不给pointer数组中顶点的个数?
不需要,跟顶点没关系,只要取找就行。
glNormalPointer()参数中没有size:固定是3
问题:物体需要不断的刷新,画一次传一次:需要:内存-》显存
Buffer Object:一般在shader中。有可能会加速10倍。
- 画物体的时候只需要传一次,驻留在显存,免去了重复性传输的耗费。两者一般配合起来使用。
Display list,一次性传入显存,把命令驻留在显存,在过去非常重要。被固化在了流水线上。从一个命令函数的存储和优化入手来提高效率。
Opengl3.1之后被废弃。大概加速不到一般的时间。
Vertex shader 和 fragment shader
Shader 语言:
- GLSL
- Cg: C for Graphics
- HLSL :high level
Shader 只能做一部分作用:
Vertex shader:
- 几何变换
- 投影变换
- 光照计算
- …对顶点属性的各种变化
fragment shader
- 纹理贴图
- 主要色和辅颜色汇合
- 基于像素的雾应用
- 改变片元的颜色属性
何为主颜色、辅颜色?
- 设置辅颜色是为了,将Specular颜色与Ambient、Diffuse 颜色分开
- 典型的光照计算中,分别计算Ambient, Diffuse,Specular和自发光的值,然后进行叠加,而在这之后进行纹理映射的话,specular可能被覆盖(被弱化,实际上高光是很重要的)。
- 为了解决这个纹理,可以将主颜色和辅颜色分开。调用glLightModelfv()
- 这样每个顶点光照计算将产生两种颜色,主颜色和辅助颜色,前者包含非镜面反射光照的总贡献,后者是所有镜面反射光照的总贡献
- 纹理映射的时候只讲主颜色和纹理颜色混合起来,执行完纹理映射之后,再将主颜色和纹理映射的混合结果与辅助颜色混合起来。
各种片元测试和操作
可以交给固定管线去做。
discard来丢弃不想绘制出来的片段:表明片元被丢弃了
if (vColorValue < 0.1f) discard;
glew -》opengl扩展库
装载-编译-链接vs,fs程序代码
如果shader 出错了但是还是会画出来,因为opengl还是会走固定流水线。
VS和FS之间的数据流
VS的输入:针对顶点的属性:一致变量(uniform)
VS的输出:针对顶点的数据
FS的输入:针对片元的属性,一致变量(uniform)
FS的输出:glFragColor;glFragDepth;…
我们的Vertex Shader的处理只需要对单个顶点做处理。GPU会把这一段程序放到GPU里面一块儿处理,有可能是并行。Fragment 同理。
VS:对单个顶点的操作;
FS:对单个片元的操作;
数据流是单向的,VS无法调用FS的数据。
VS与FS的输入与输出变量
VS的输入变量:
内建的uniform变量:系统本身就有的,如:gl_ModelViewMatrix;
shader编程并没有脱离默认的固定流水线。自动调用。Access = readOnly
内建的顶点属性变量(“in” 类型) 如:gl_Vertex
自己定义的"uniform"类型变量
Access = readOnly自己定义的"in"类型数据
纹理数据
VS的输出变量
VS输出的特定变量(Special output Variables),不用与fragment的输出
Access = ReadWrite
VS输出的内建顶点属性变量(加out限定符号)
Access = ReadWrite,往往和fs的输出相对应
VS输出的自定义顶点属性变量(加out限定符)
FS的输入变量
内建的uniform状态变量
内建的"in"类型变量,如gl_Color
Access = ReadOnly
自己定义的"uniform"类型变量
自己定义的"in"类型变量
纹理数据
FS的输出变量
- gl_FragColor
- gl_FragDepth:一般,DEFAULT = gl_FragCoord.z;
- gl_FragData[gl_MaxDrawBuffers],关注下多目标输出。mutly Target
- Access = readWrite
变量前的限定符
- uniform :一致型变量
- in
- out
- attribute:顶点的属性变量(glsl 1.40已经废除),对应uniform
- varying:易变性变量(glsl 1.40已经废除)
GLSL的基本语法
学习shader,最重要的是明白它在流水线中的位置,流水线中的数据流的怎么样的
数据类型及其使用
使用:声明、定义、初始化、赋值、访问
与C语言类似,定义一个变量的同时可以进行初始化
glsl是强制类型语言:必须进行显示的强制类型转换,不存在隐式的类型转换。 如:int b = int(2.0);
基本数据类型:
float、 int、bool、uint
向量类型(vec),非常灵活。
Vec4 color;
Vec3 rgb = vec3(color); // 可以直接截取。
访问:
- 下标方式 float posY = a[1]
- 名称方式(选择子方式):为了可读性
- x、y、z、w
- r、g、b、a
- s、t、p、q
- 搅拌式访问
- 向量乘法
- 点积(dot)和叉积(cross)
矩阵类型(mat)
- 只有浮点型
- 矩阵是列优先顺序,一列一列来
- 非常灵活
数组
- 在glsl中,可以声明和访问数组
- 只支持一维数组
- 下标不能为负数
- 数组的初始化与访问
- float coeff[3] = float[3] (1.0, 2.1, 3.5);
- glsl数组提供了一个隐式 的方法length()
- for(int i = 0; i < coeff.length();i++)
- {
- coeff[i] *= 2.0;
- }
结构体
struct dirlight
{
vec3 direction;
vec3 color;
}
dirlight light = dirlight(vec3(1.0), vec3(0.8));
sampler类型
变量前的限定符
- const
- uniform:readonly
- in :ro
- out:rw
- centroid, smooth, flat, nonperspective:限定插值方式
- attribute, varying — 已经废除
内建函数
用VS实现光照计算
blin光照模型计算公式:
Grourand Shader
可从外部输入的uniform变量:
Ka、Kd、Ks、n
Ia、Id、Is
灯的位置,视点的位置
以上全是内建变量
需要计算的变量
- N:顶点的法相,不能直接用,需要进行变换,因为物体在变化。
- L:当前顶点的光照方向
- H:是V和L的角平分线,V是视线方向,计算H时,需要先计算出视线方向v
视点不变,在圆心
法向:
normal的变换和顶底的变换不一样,注意,详细红宝书中有说。
计算H,H.L, H.H,向量处理后需要标准化,才能点积。
NdotL 小于 0,说明在背面。
用FS实现逐像素光照计算
Phong shadering ,计算每个顶点的法向。
卡通风格的光照实现
实际上我们会发现,光被分层了。
造型技术
图形对象
基本元素:
点,线,面,体等几何元素经过平移,放缩,旋转,等几何变换。
实体的性质:
- 刚性
- 维数的一致性
- 占据有限的空间
- 边界的确定性
- 封闭性
物体表面的性质:
- 连通性
- 有界性
- 非自相交性
- 可定向性
- 闭合性
样条
曲线描述;
分形几何
特点:
- 不规则
- 自相似
分形维数
生成过程:为产生物体局部细节指定一个过程
- 初始生成元
- 生成元
形状语法:
粒子系统:
模拟火,爆炸, 烟, 云,等微小物体的集合。
粒子生成要素:
- 例子本身的造型
- 例子的运动方向
公告板技术:永远只能看到牌子的正面,可以理解为(牌子永远跟着相机转)
计算机图形学入门OpenGL相关推荐
- OpenGl计算机图形学入门
OpenGl计算机图形学入门 前言 一.OpenGl及GLSL 二.语言 1.C++及C++应用程序 (1)C++ (2)C++应用程序 三.库 1.GLFW 窗口管理库 2.GLEW 扩展库 3.G ...
- Games101计算机图形学入门基础之一:向量变换、矩阵变换、视图变换、欧拉角与四元数
Games101计算机图形学入门基础之一:向量变换.矩阵变换.齐次坐标.欧拉角与四元数 目录 Games101计算机图形学入门基础之一:向量变换.矩阵变换.齐次坐标.欧拉角与四元数 前言 向量 向量加 ...
- 计算机图形学入门-线性代数复习
本专栏内容整理了GAMES101的计算机图形学课程的主要内容,作为我学习计算机图形学的一份复习备份或叫做笔记.内容中如有错误,或有其他建议,欢迎大家指出. 附上GAMES101计算机图形学课程:GAM ...
- 平移计算机图形学代码注释,求代码注释:计算机图形学的OpenGL画四面体。高手来吧。争取每句都注释下。谢谢...
求代码注释:计算机图形学的OpenGL画四面体.高手来吧.争取每句都注释下.谢谢 答案:3 信息版本:手机版 解决时间 2018-12-20 05:00 已解决 2018-12-20 01:58 求 ...
- Games101计算机图形学入门基础之二:光栅化
Games101计算机图形学入门基础之二:光栅化 引言 三角形的离散化 采样 走样 走样带来的瑕疵 反走样 先模糊再采样 傅里叶变换 低通滤波 卷积 多重采样抗锯齿(超采样) 深度缓存 可见性与遮挡( ...
- 计算机图形学入门(一)-线性代数部分知识1
本部分主要介绍了向量的点乘与叉乘在图形学中的基本应用,介绍了图形学中常用的2D矩阵变换,例如缩放.对称.切变换.旋转.平移.逆变换.组合变换和分解变换.还有在图形学中为了简化操作而采取的添加维度的方法 ...
- 计算机图形学入门(二)-线性代数部分知识2视图变换和投影变换
本章主要进行了视图/相机变换的求解,和两种投影变换的求解(正交投影.透视投影),并且简单的完善了3D旋转变换的介绍. 主要的学习过程来自下面的视频,本文只会有主要的基础内容,默认一些知识大家是掌握的, ...
- GAMES101-现代计算机图形学入门-闫令琪——Lecture 05 Rasterization 1 (Triangles)
GAMES101-现代计算机图形学入门-闫令琪--Lecture 05 Rasterization 1 (Triangles) 目录 GAMES101-现代计算机图形学入门-闫令琪--Lecture ...
- 计算机图形学及OpenGL简介摘记
计算机图形学(Computer Graphics)是研究怎样用计算机表示.生成.处理和显示图形的一门学科.图形的具体应用范围很广,但是按基本的处理技术划分只有两类.一类是线条,如工程图.地图.曲线图表 ...
最新文章
- 系统架构师具备的能力和构架师自我培养过程
- spring mvc 自动生成代码
- 树-堆结构练习——合并果子之哈夫曼树
- idea + maven + profile + tomcat 调试 javaee 和js
- (74)分析 APC 插入过程 —— KeInsertQueueApc , KiInsertQueueApc
- filter in Fiori HANA live report
- html页面icon字体无法显示,bootstrap中icon字体图标怎么不显示?
- Hibernate之Hello World篇
- 登录iis7 网站服务器上网站网页可以访问 但远程访问网站网页不能访问的解决方法
- 杭电2540遮挡判断
- PySide QtCore.Signal帮助手册
- python爬取大众点评数据_爬虫爬取大众点评评论数
- Coverity代码静态检测工具介绍
- Cadence中的homogeneous与hetergeneous的区别及注意事项
- 计算机应用的最广领域,从乡镇企业的从业人员数,我们可以看出:
- 基于Jeecg的权限获取
- 思科模拟器(cisco) 交换机综合实践(笔记篇)
- 携程跨团队敏捷项目实战
- 基于ServiceStage的微服务开发与部署(二)
- 以太坊是什么? 以及以太坊如何工作的?
热门文章
- error:expected '{' at end of input
- httPs//yy7com/a1phP,header.php
- Spring IOC循环依赖问题
- 神经网络的前向和反向传播
- 微积分--极值点不一定是升降分界点、升降分界点一定是极值点
- 前后端分离的前生今世
- c++找出1000以内的完数
- Struts tags详解(Data tags)
- 计算机教育随笔,计算机信息教育随笔
- 上海30K*16和深圳22K*14的offer同时揽下,真的是行情不好吗?