当前位置:   article > 正文

关于opengl 编程指南中使用几何着色器渲染毛发的分析_opengl渲染毛发

opengl渲染毛发

源码下载:https://github.com/openglredbook/examples
1、程序的初始化
从哪里入口的?
宏定义发挥了及其重要的作用,有一定的迷惑性。

DEFINE_APP(FurApplication, "Fur Rendering")
  • 1

首先:FurApplication是一个类,如何定义这个类:
也是使用的宏定义:

BEGIN_APP_DECLARATION(FurApplication)
END_APP_DECLARATION()
  • 1
  • 2

传入的FurApplication,被展开之后,得到的是:class FurApplication:pulic VermilionApplication

#define BEGIN_APP_DECLARATION(appclass)                     \
class appclass : public VermilionApplication                \
{                                                           \
public:                                                     \
    typedef class VermilionApplication base;                \
    static VermilionApplication * Create(void)              \
    {                                                       \
        return (s_app = new appclass);                      \
    }

#define END_APP_DECLARATION()                               \
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

宏定义没啥难的,就是替换。

下面是看入口的地方:DEFINE_APP(FurApplication, “Fur Rendering”)
在这里插入图片描述
这个主循环是WinMain发起的。

2、第一个pass的vertex

#version 410
layout (location = 0) in vec4 position_in;
layout (location = 1) in vec3 normal_in;
layout (location = 2) in vec2 texcoord_in;
uniform mat4 model_matrix;
uniform mat4 projection_matrix;
out VS_FS_VERTEX
{
	vec3 normal;
} vertex_out;
void main(void)
{
     vertex_out.normal = normal_in;
     gl_Position = projection_matrix * (model_matrix * position_in);
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

第一个处理顶点的程序,输入的数据有顶点位置、法线、纹理坐标;M和P矩阵
输出的数据包在一个块中:顶点位置、法线、纹理坐标。没有做任何的变换,其中内置变量gl_Position用于接收本地坐标。

3、第一个pass的fragment

#version 410
layout (location = 0) out vec4 color;
in VS_FS_VERTEX
{
   vec3 normal;
} vertex_in;

void main(void)
{
     vec3 normal = vertex_in.normal;
     color = vec4(0.2, 0.1, 0.5, 1.0) * (0.2 + pow(abs(normal.z), 4.0)) + vec4(0.8, 0.8, 0.8, 0.0) * pow(abs(normal.z), 137.0);
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

为何要做这种颜色的计算,不明白。

4、第二个pass的vertex

#version 410
layout (location = 0) in vec4 position_in;
layout (location = 1) in vec3 normal_in;
layout (location = 2) in vec2 texcoord_in;
out VS_GS_VERTEX
{	
	vec3 normal;
	vec2 tex_coord;
}vertex_out;

void main(void)
{
      vertex_out.normal = normal_in;
      vertex_out.tex_coord = texcoord_in;
      gl_Position = position_in;
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

第二个处理顶点的程序,输入的数据有顶点位置、法线、纹理坐标。
输出的数据包在一个块中:顶点位置、法线、纹理坐标。没有做任何的变换,其中内置变量gl_Position用于接收本地坐标。

5、第二个pass的geometry

#version 410
layout (triangles) in; 
layout (triangle_strip, max_vertices = 120) out;
uniform mat4 model_matrix;
uniform mat4 projection_matrix;
uniform int fur_layers = 30;
uniform float fur_depth = 8.0;
in VS_GS_VERTEX
{
      vec3 normal;
      vec2 tex_coord;
} vertex_in[];

out GS_FS_VERTEX
{
     vec3 normal;
     vec2 tex_coord;
     flat float fur_strength;
} vertex_out;

void main(void)
{
	int i, layer;
	float disp_delta = 1.0 / float(fur_layers);
	float d = 0.0;
	vec4 position;
	for (layer = 0; layer < fur_layers; layer++)
    {
	        for (i = 0; i < gl_in.length(); i++) 
	        {
	       		vec3 n = vertex_in[i].normal;
	        	vertex_out.normal = n;
	        	vertex_out.tex_coord = vertex_in[i].tex_coord;
	        	vertex_out.fur_strength = 1.0 - d;
	       		position = gl_in[i].gl_Position + vec4(n * d * fur_depth, 0.0);
	        	gl_Position = projection_matrix * (model_matrix * position);
	      		EmitVertex();
	       }
	       d += disp_delta;
	       EndPrimitive();
    }
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

几何着色器的输入图元的定义我三角形;
输出定义为三角条带,输出的最大顶点数限制为120;
四个uniform变量,用于cpu传入shader,外面设置使用;
其中fur_layers定义了分层的数量;也就是毛发有几段构成;
fur_depth定义了毛发的长度;
VS_GS_VERTEX接口块,定义了由vs到gs的输入数据结构,这个参考上面vertex shader的输出。
不同的是,这里的输入是一个数组vertex_in[];
GS_FS_VERTEX和VS_GS_VERTEX数据成员一样,但是多了一个成员:fur_strength,后面使用。
还有一个不同的是GS_FS_VERTEX的是它的实例化成员不是数组,还是一个vertex_out变量。

再接着就是main函数了。
disp_delta = 1.0 / float(fur_layers);
for第一层是枚举每层;
for第二层是枚举图片的三个输入点;
对于每个点,进行处理;
for (i = 0; i < gl_in.length(); i++) 为啥不是使用vexter_in[]呢?其实也可以。他们的length是相同的。
vec3 n = vertex_in[i].normal;
vertex_out.normal = n;
vertex_out.tex_coord = vertex_in[i].tex_coord;
初始化每个点的成员。
vertex_out.fur_strength = 1.0 - d;
后面处理完一层之后对d进行d += disp_delta;处理。也就是说层数越多的时候,fur_strength越小。

position = gl_in[i].gl_Position + vec4(n * d * fur_depth, 0.0);
这个是在原有的local顶点位置,沿着法线方向增加了:d*fur_depth增量。
比如分成4层,fur_depth=10,那么每层是10/4=2.5长度。
然后对这个点进行mvp处理:gl_Position = projection_matrix * (model_matrix * position);
注意这里的view矩阵没有,默认就是单位矩阵了。
EmitVertex();最后进行发射这个顶点,于是乎在原顶点的沿着法线的方向生成了一个新的顶点。
然后进行: d += disp_delta;处理。
第二层for结束之后,也就结束了当前图元三角形的处理了。调用EndPrimitive()结束当前图元的处理。于是多了一个三角形了。如下图:

在这里插入图片描述
一层处理好之后,就多了上面的三个红色的点了。
其他层就再类似处理了。但是最大是我们限定的layout (triangle_strip, max_vertices = 120) out;120个顶点。

6、第二个paas的fragment

#version 410
layout (location = 0) out vec4 color;
uniform sampler2D fur_texture;
uniform vec4 fur_color = vec4(1, 1, 0, 1.0);
in GS_FS_VERTEX
{
     vec3 normal;
     vec2 tex_coord;
     flat float fur_strength;
} fragment_in;
void main(void)
{
      vec4 rgba = texture(fur_texture, fragment_in.tex_coord);
      float t = rgba.a;
      t *= fragment_in.fur_strength;
      color = fur_color * vec4(1.0, 1.0, 1.0, t);
 }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

去除纹理颜色,对其alpha值进行层的渐变,层越高,颜色越浅。

7、第一个shader的绘制

glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
将擦除颜色设置为黑色;
glClearDepth(1.0f);
将擦除深度设置为1.0,最大值就是1.0了,最小值为0;
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
开始真正的擦除,颜色缓冲用黑色擦除;深度缓冲用1.0擦除。

glDisable(GL_BLEND);
禁用混合;

glEnable(GL_CULL_FACE);
启用剔除
glCullFace(GL_FRONT);
剔除的是前表面
glEnable(GL_DEPTH_TEST);
启用深度测试
glDepthFunc(GL_LEQUAL);
设置深度测试函数<=才通过
最后开始绘制:
object.Render();

8、第二个shader的绘制

glEnable(GL_BLEND);
开启混合
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
设置混合的函数为src,one-src

glDepthMask(GL_FALSE);
禁止深度写入,以为要混合;

开始绘制:
object.Render();

最后恢复设置:
glDepthMask(GL_TRUE);
glDisable(GL_BLEND);

如果没有禁用深度写入,则是如下的左图效果,正确的是右图效果:
在这里插入图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/很楠不爱3/article/detail/108555
推荐阅读
相关标签
  

闽ICP备14008679号