kanzi&opengl杂谈

版权声明:本文为博主原创文章,欢迎指点!!! https://blog.csdn.net/allen807733144/article/details/83865529

最近有幸总结了一下kanzi中opengl的东西,实际比较重点的也就是shader部分,对于其他部分,这里也会简单的说,但是具体的功能作用就要自己下来慢慢研究了

一:引言

本文章主要介绍kanzi和opengl之间的一些问题,可能会比较杂,因为很多东西也无法预测他们具体是如何结合,故我们会以kanzi为主,在kanzi的基础上介绍opengl的一些东西。

通过kanzi help doc中的opengl接口,我们可以了解kanzi也是支持modern opengl,故老的opengl接口是不支持的。具体接口可见<system/wrappers/kzs_opengl.h>

 

这里的东西比较杂,我们不会具体的说,这里会分几个大块来针对性的说明。

 

二:具体介绍

一:着色器

一:shader(着色器)

着色器(Shader)是用来实现图像渲染的,用来替代固定渲染管线的可编辑程序。其中Vertex Shader主要负责顶点的几何关系等的运算,Pixel Shader(Fragment Shader)主要负责片源颜色等的计算。

着色器替代了传统的固定渲染管线,可以实现3D图形学计算中的相关计算,由于其可编辑性,可以实现各种各样的图像效果而不用受显卡的固定渲染管线限制。

目前比较流行的Shader语言除了GLSL之外还有HLSL,RM等,当然,我们目前主要使用GLSL。

Opengl的绘图管线

当然,这部分opengl的部分这里只是简单介绍。我们只需知道整个工作流程即可。

 

  1. 准备顶点数据:

在CPU中进行。这里会定义一些VAO和VBO,他们包含了每个一些顶点数据的信息,在shader中的体现即attribute。

在kanzi中,我们定义的EmptyNode2D或者Image2D...在这部分的体现即会准备不重复的4个点即重复的6个点,构成2个三角形。然后会将这些点绑定到VAO或者VBO中,发送给GPU。

 

  1. Vertex Processing

调用Vertex Shader处理顶点数据信息。这个阶段会接受传递过来的attribute数据作为输入,然后通过一些转换后输出。每个顶点都是1对1。

Kanzi中我们可以看到attribute属性的有kzPosition,kzTextureCoordinate0及其他。

 

  1. Primitive Assembly

图元装配:即把vertex shader输出的顶点数据集合到一起,并把它组合成一个图元的过程。这个输出的类型由用户决定,一般输出的是三角形,这个取决于你drawcall的类型。

 

  1. Geometry Shading: 这个目前不做介绍,它即对Primitive Assembly输出的图元处理后再输出图元。

 

  1. Rasterization

光栅化:一系列处理后的图元会被光栅化为一系列的‘候选像素点’。

  1. Fragment Processing

调用Fragment shader处理像素点。处理后我们只需要得到out的像素即gl_FragColor。

  1. Pre-Fragment :
  2. 生成frame buffer后GPU显示出来。

 

这部分东西很多,而我们应该了解的东西总结一下:

  1. Fragment Shader处理像素点,故一些运算如果可以放到Vertex shader中最好。
  2. 变量的属性:

Attribute:输入的定点属性,无法改变。

Uniform:用户定义的属性,可在外部改变。

Varying:在vertex shader和fragment shader传递变量

  1. Float精度

highp:

mediump:

lowp:

使用默认精度可加precision mediump float;

  1. 着色器尽量简单。

 

二:brush及Material

Kanzi中,如果需要对自带的2D节点添加材质,则需要一个中介brush。

如果我们需要在一个Node2D上画出某个东西(颜色,材质或者图片),我们需要借助一个brush来操作。

伪代码:

  1. 使用Material Brush:

Material::Create();

Shader::Create()

Shader->InitializeFromMemory()

Material->setShader()

MaterialBrush::Create(Material)

BrushResource = kzuBrushResourceCreateFromBrush(MaterialBrush)

Node->setBackGroundBrush(BrushResorce)

 

  1. Color Brush和TextureBrush同理。

 

故思考:如果我们需要提高开机速度,那么如果这个节点刚好有Material或者其他,那么我们可将这部分的内容拆开放到后面处理。测试,添加material和不加material加载速度确实是有差异的。

 

 

材质:通俗的将就是材质决定我们想要的物体呈现什么样子。可以理解为是编译shader后生成的一个实例,材质的属性就是shader uniform开放的变量。所以我们通过材质的属性可以达到我们想要的效果。

 

在kanzi中,shader这部分我们无法输入attribute,所以在我们操作范围内只能去改变out color。在其中,我们可以添加uniform去控制我们想要达到的效果。

在编写shader过程中,考虑到帧率因素,有以下优化方案:

  1. 较少甚至禁用 uniform varying的if或者for。
  2. 由于gpu中的运算时并行运算,所以在有if...else判断中,会根据最慢的执行计算,所以我们需减少if...else执行时间。
  3. GPU中计算实际为矩阵运算,而float在计算时相当于也会转换为四维矩阵的运算,所以我们应当减少运算的次数。
  4. 如果是透明或者纯色,我们可直接使用color,减少取纹理。
  5. 除法运算尽量转为乘法运算。
  6. 在运算过程中,注意一些复杂的运算符,比如pow,exp,sin,cos,log,tan等。
  7. Float的精度可根据实际情况使用精度低的类型
  8. 在计算一些顶点等信息时,能放到vertext中的尽量放到vertextshader中运算。
  9. 在运算过程中,避免有所依赖。这样并行运算可能会是同步的

 

扩展:

  1. Blend Mode:
    1. Opaque:不开启混融效果
    2. Automatic:根据Project属性的预设值选择Premultiplied或者Non-Premultiplied,默认是Premultiplied.
    3. Premultiplied:实际调用glBlendFunc(GL_ONE,GL_ONE_MINUS_SRC_ALPHA)
    4. Non-Premultiplied:调用glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
    5. Mixed:调用glBlendSeparateFunc(SRC_ALPHA, ONE_MINUS_SRC_ALPHA, ONE, ONE_MINUS_SRC_ALPHA)
    6. Additive:调用(GL_ONE, GL_ONE):实际就是两种颜色相加,比如(1,0,0)和(0,1,0)相glBlendFunc加得到(1,1,0)
  2. BlendIntensity:
    1. .rgba*BlendIntensity
    2. .a*BlendIntensity

二:模型

在项目中,很容易看到模型,表盘,车模等等。先说一下模型的构成:

而一起提供过来的还有一张图片,这张图片就是覆盖在模型表面的纹理。而我们看到的图片的形状和模型一般不同,这方面我们不需要关心,模型到底是取得图片的uv到底在哪也不关我们的事,我们只需要做的就是看后期可能会如何处理它的UV。

例:在项目表盘中,我们很容易看到模型,它到底是怎么工作的?

如何导入模型不做介绍,我们可以在kanzi中看到mesh管理器中有对应的mesh,每个mesh的信息也可以看到,三角形数量和顶点的数量、fbx名称、材质等等。

而对于模型的使用和kanzi中3d节点大同小异。

渲染模型流程:

首先,模型除了我们可以操作坐标之外,kanzi中我们是改变不了一个mesh内部的顶点结构的,一个mesh在kanzi中的体现就是model3D。所以在通过材质渲染node时,渲染流程即上面所述流程。顶点以attribute在Vertex shader作为输入,经过转换为世界坐标后输出进行图元装配、光栅化等。Vertex中还会包含uv,这个uv即纹理的坐标(-1, 1)。那在这部分我们是否可以修改这个mesh的内部结构了?

其次,在fragment中,模型已经是固定的,而类似于表盘中的扫光实现原理是什么呢?我们知道在fragment中的运算实际就是计算像素点,将输出的像素表现在模型对应的位置。所以对于表盘中的扫光,我们能看到进度的实际是通过控制纹理的uv坐标让颜色显示出来,而未显示的部分就是一个vec4(0.0)或其他颜色。

最后,对于模型,三角形越多越细致,而导致越占资源;对于纹理,分辨率越大越好,但是影响效率,所以在模型这块就需取一个适当的值。

 

三:光照

目前项目中用到光照的东西很少,一般像车模可能会加入光照。

首先光照类型:点光,平行光(太阳光),聚光(手电筒)

这些光在kanzi中都有定义,但是具体实际上也是在shader中去模拟光照的一系列计算。

虽说光是一系列的计算,但是opengl对这些光都有固定的说明。我们这里会针对kanzi中的东西做一个说明,其实也和opengl类似。

如何使用光不做介绍,我们说说材质中的光照。

Kanzi给我们提供了很多的光照类型的材质:

Phong:只包含光照

PhoneCube:包含光照和环境纹理

PhongTexture:包含光照和纹理

PhongTextureCube:包含光照、纹理和环境纹理。

对于这几种材质,都有共同属性就是针对光照的:

Ambient Color:环境光颜色

Diffuse Color:漫反射颜色

Emissive Color:反射颜色

Specular Color:镜面反射颜色

Specular Exponent:镜面反射系数

这几种属性可改变光的颜色以及一些光的效果。但是很多我们要实现的效果不仅仅是光可以做完的,我们也许在模型表面需要加入材质,于是我们使用PhongTexture,这个是在属性多了一个Texture属性,我们需要加入环境的颜色比如水面呈现天空的东西,于是我们再加入Cube。对于Cube,我们下节纹理介绍。

 

四:纹理

这部分其实也没什么东西可说,我们这里说一下压缩纹理和环境纹理。

压缩纹理:目前kanzi支持压缩的纹理类型:ATC、ETC、PVRTC。

很多时候我们用的都是PNG类型生成的纹理,而使用压缩纹理往往是资源可能很大或者影响到性能,但是使用压缩纹理的类型我们需要确定机器是否支持。

支持opengl es2.0(3.0)的是支持ETC1(2)的。我们机器支持的版本是 OpenGL ES 3.0(具体的可通过demo或者kanzi中的opengl接口查看),故我们在使用的时候可压缩成etc格式的压缩纹理,其他格式目前不清楚。

在kanzi中找到resource file->image,在对应图片属性页选中要压缩的目标格式,右键创建纹理即可。

环境纹理:游戏中有天空盒这个东西,但是kanzi中目前是无法使用的,我们可以使用环境纹理。原理:利用环境的颜色/纹理后映射到目的模型上。

环境纹理由六张图片生成,刚好是构成一个立方体。

在kanzi中创建一个环境纹理:Textures->create cube-map texture,然后再属性框添加这 六张图片(注意环境纹理的前后左右上下顺序)。

注:arm支持的最大纹理尺寸:8192

kanzi支持导入的最大纹理尺寸:4096

五:3D

项目中3D场景虽然不多,但是基本每个项目多多少少都包含一些,所以粗略的探讨一下opengl 3d部分在kanzi中的一些东西。

一:深度测试

深度:该像素点距3d中的摄像机的距离(绘制坐标),深度缓存存储着深度值。

在没有深度测试时一般都是按照顺序绘制,这样会把某些物体覆盖,所以深度测试会根据Z值(远近)来正常显示。

opengl默认不会启用深度测试,但是默认开启深度测试。

glEnable(GL_DEPTH_TEST)

绘制半透明物体不适用。

Kanzi中默认属性如上图所示。启用了深度测试,如果没有通过深度缓存,则会丢弃该片段,还需在每个渲染迭代之前清除深度缓存,否则仍然在使用上一次的深度值。

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)

禁用深度缓冲的写入在kanzi中没找到如何做,这种一般使用一个只读的深度缓冲。

二:Frustum Culling

将不可见的物体提前剔除。

三:Face Cull

面剔除。

剔除背向面,前向面,不启用剔除。

Opengl默认不启用面剔除,而kanzi默认启用背面剔除,意思显而易见就是背面的面会丢弃不渲染。

四:模板测试

片段着色器处理完一个片段之后,经过深度测试丢弃一些片段,被保留的片段进入模板测试,他可能会丢弃更多。深度测试根据深度缓冲进行,而模板测试也是根据模板缓冲进行。

模板缓冲会首先被清除为0,之后再模板缓冲中用1填充,然后在场景中的片段只会在模板值为1时被渲染,剩下的都会被丢弃。

Kanzi默认不启用模板测试,如果启用模板测试,一般有如下步骤:

  1. 启用模板缓冲的写入
  2. 渲染物体,更新模板缓冲的内容
  3. 禁用模板缓冲的写入
  4. 渲染其他物体,根据模板缓冲的内容丢弃特定的片段。

 

在每次迭代之前清除模板缓冲。

Stencil Fail Operation: 模板测试失败时的行为

Stencil Pass Depth Fail Operation: 模板测试通过,但深度测试失败时采取的行为

Stencil Pass Depth Pass Operation: 模板测试和深度测试都通过时采取的行为

 

行为具体有下面

Keep:保持当前存储的模板值

Zero:将模板值设置为0

Replace:将模板值设置为参考值(Stencil Function Reference Value)

Increase:如果模板值小于最大值则将模板值加1

IncreaseWrap:如果模板值超过了最大值则归零

Decrease:如果模板值大于最小值减1

DecreaseWrap:如果模板值小于0则将其设置为最大值

Invert:按位翻转当前的模板缓冲值

opengl和kanzi默认的行为都是keep。

 

Stencil Function:应用到已存储的模板值上的选项:

Always:永远通过测试

Never:永远不通过测试

Less:在片段模板值小于缓冲的模板值时通过测试

Lequal:在片段模板值小于等于缓冲区的模板值时通过测试

Greater:在片段模板值大于缓冲区的模板值时通过测试

Gequal:在片段模板值大于等于缓冲区的模板值时通过测试

Equal:在片段模板值等于于模板的模板值时通过测试

Notequal:在片段模板值不等于缓冲区的模板值时通过测试

Opengl默认GL_LESS,kanzi默认always。

五:裁剪测试

当启用scissor test时,会将渲染时的视口做限制,也就是我们处理的像素范围做了限制。

 

六:混合

七:抗锯齿

目前了解的在kanzi中做抗锯齿都是从shader入手还有多重采样。

 

转载请注明出处:https://blog.csdn.net/allen807733144

猜你喜欢

转载自blog.csdn.net/allen807733144/article/details/83865529