全球图形学领域教育的领先者、自研引擎的倡导者、底层技术研究领域的技术公开者,东汉书院在致力于使得更多人群具备内核级竞争力的道路上,将带给小伙伴们更多的公开技术教学和视频,感谢一路以来有你的支持。我们正在用实际行动来帮助小伙伴们构建一套成体系的图形学知识架构,你在我们这里获得的不止于那些毫无意义的代码,我们这里更多的是代码背后的故事,以及精准、透彻的理解。我们不会扔给人们一本书或者给个思路让人们去自学,我们是亲自来设计出好的课程,让人们明白到底背后还有哪些细节。
这里插播一个引擎大赛的消息,感兴趣的同学可以看一眼,这也是东汉书院的立项使命:
东汉书院:自研引擎大赛zhuanlan.zhihu.com
Because points in OpenGL are rendered as axis-aligned squares, rotating the point sprite must be done by modifying the texture coordinates used to read the sprite’s texture or to analytically calculate its shape. To do this, you can simply create a 2D rotation matrix in the fragment shader and multiply it by gl_PointCoord to rotate it around the z axis. The angle of rotation could be passed from the vertex or geometry shader to the fragment shader as an interpolated variable. The value of the variable can, in turn, be calculated in the vertex or geometry shader or be supplied through a vertex attribute. Listing 9.34 shows a slightly more complex point sprite fragment shader that allows the point to be rotated around its center.
由于OpenGL在绘制点精灵的时候会按照轴对齐的方式去绘制四边形,所以如果你想旋转一个点精灵的话就必须要修改点精灵的纹理坐标或者去亲手计算它的形状。为了做到这一点,你可以简单粗暴的创建一个2D的 旋转矩阵,然后在fragment shader中使用它与gl_PointCoord相乘来让点精灵绕z轴旋转。输入的旋转角度可以从vertex shader或者是geometry shader中传入,然后插值得到。这样一来,这个变量的值就可以在 vertex shader计算得到或者geometry shader计算得到或者通过顶点的属性提供。清单9.34展示了一个相对更复杂一点的点精灵的fragment shader,它使得点精灵可以绕着它的中心点旋转。
#version 450 core
uniform sampler2D sprite_texture;
in float angle;
out vec4 color;
void main(void)
{const float sin_theta = sin(angle);const float cos_theta = cos(angle);const mat2 rotation_matrix = mat2(cos_theta, sin_theta, -sin_theta, cos_theta);const vec2 pt = gl_PointCoord - vec2(0.5);color = texture(sprite_texture, rotation_matrix * pt + vec2(0.5));
}
Listing 9.34: Naïve rotated point sprite fragment shader
This example allows you to generate rotated point sprites. However, the value of angle will not change from one fragment to another within the point sprite. That means sin_theta and cos_theta will be constant and the resulting rotation matrix constructed from them will be the same for every fragment in the point. It is therefore much more efficient to calculate sin_theta and cos_theta in the vertex shader and pass them as a pair of variables into the fragment shader rather than calculating them at every fragment. Here’s an updated vertex and fragment shader that allows you to draw rotated point sprites. First, the vertex shader is shown in Listing 9.35.
这个例子允许你去生成旋转的点精灵。然而,这个角度的值不会因为点精灵上所在像素位置的变化而变化。也就是说sin ——theta和cos_theta将是常量并且产生的结果旋转矩阵对于每个点上的所有像素来说都是一样的。 因此,相比在fragment shader中进行计算,在vertex shader中计算出sin_theta和cos_theta的值然后把它们传入fragment shader会更加的高效。下面展示了新版本的vertex shader和fragment shader的代码。首先, 清单9.35展示了vertex shader的代码。
#version 450 core
uniform matrix mvp;
in vec4 position;
in float angle;flat out float sin_theta;
flat out float cos_theta;
void main(void)
{sin_theta = sin(angle);cos_theta = cos(angle);gl_Position = mvp * position;
}
Listing 9.35: Rotated point sprite vertex shader
Next, the fragment shader is shown in Listing 9.36.
紧接着,清单9.36展示了fragment shader的代码
#version 450 core
uniform sampler2D sprite_texture;
flat in float sin_theta;
flat in float cos_theta;
out vec4 color;
void main(void)
{mat2 rotation_matrix = mat2(cos_theta, sin_theta, -sin_theta, cos_theta);vec2 pt = gl_PointCoord - vec2(0.5);color = texture(sprite_texture, rotation_matrix * pt + vec2(0.5));
}
Listing 9.36: Rotated point sprite fragment shader
As you can see, the potentially expensive sin and cos functions have been moved out of the fragment shader and into the vertex shader. If the point size is large, this pair of shaders performs much better than the earlier, brute-force approach of calculating the rotation matrix in the fragment shader.
如你所见到的一样,sin和cos函数已经从fragment shader中移到了vertex shader中去了。如果点的大小很大的话,这组shader的性能相对于之前的那个简单粗暴的在fragment shader中计算旋转矩阵会更好一点。
Even though you are rotating the coordinates you derived from gl_PointCoord, the point itself is still square. If your texture or analytic shape spills outside the unit diameter circle inside the point, you will need to make your point sprite larger and scale your texture coordinate down accordingly to get the shape to fit within the point under all angles of rotation. Of course, if your texture is essentially round, you don’t need to worry about this at all.
即便你旋转从gl_PointCoord中推导出来的纹理坐标,点精灵本身依然是一个矩形。如果你的纹理或者形状玩到了点精灵的外面,你需要把点精灵弄大一点并且给它一个缩放,让它的形状适应当前点精灵的旋转角度。当然 如果你的纹理基本上是个圆的的话,你不需要担心这个事情。
我们核心关注和讨论的领域是引擎的底层技术以及商业化方面的信息,可能并不适合初级入门的同学。官方相关信息主要包括了对市面上的引擎的各种视角的分析以及宏观方面的技术讨论,相关领域感兴趣的同学可以关注东汉书院以及图形之心公众号。
只言片语,无法描绘出整套图形学领域的方方面面,只有成体系的知识结构,才能够充分理解和掌握一门科学,这是艺术。我们已经为你准备好各式各样的内容了,东汉书院,等你来玩。