很久没写shadertoy了,碰巧OpenGL的一个小作业可以纯shader完成,拿来水一篇博客好啦。 阿基米德螺旋其实之前写过👉ODOS丨大大泡泡糖👈 相当于这次坐标系统里的y坐标。
实验原理
主要效果于Fragment Shader实现。阿基米德螺旋运用坐标转换,将纹理坐标从笛卡尔坐标系转换为类极坐标系,通过时间偏移形成动态效果。
阿基米德螺旋实现 - 坐标转换
当一点P沿动射线OP以等速率运动的同时,这射线又以等角速度绕点O旋转,点P的轨迹称为“阿基米德螺线”。它的极坐标方程为:
通过构建基于阿基米德螺旋的坐标系,我们将一个点的纹理坐标
其中R为阿基米德螺线的间距。则ceil(a)
为转动的周数,完整的转动角为:
d的计算公式为:
对转换后的fract()
函数截取小数部分,即可获取采样纹理的“局部坐标系”,坐标系的可视化效果如下:
小数部分的xy做为纹理坐标,用于采样star.bmp
,d的整数部分作为生成随机颜色的seed
.
用转换过的坐标系采样纹理,就可以得到沿阿基米德螺旋旋转的星星(如左下)。
为了更高的体现“旋转并扩散”的效果,利用上述的d整数部分通过step()
制作了一张随时间旋转扩散的“蒙版”(如中下),与星星相乘后即可获取中心部分(如右下)。
随机颜色实现
Shader中的伪随机函数选用了最常见的正弦伪随机。将上文提到的d
整数部分作为随机种子,并对RGB三个通道添加了不同的偏移值,以获取随机色。
float random (vec2 uv)
{
return fract(sin(dot(uv.xy, vec2(12.9898,78.233))) * 43758.5453123);
}
存在的问题和掩盖方式
通过Shader完成该题目的方式的优点在于不需要维护粒子系统的产生、死亡,由于只有一个四边形所以顶点极少,属于偷懒的小trick...但极坐标的扭曲会导致靠近螺旋中心的星星产生比较明显的扇形变形。
为了掩饰中心变形,在中心叠加了一颗同系统一起旋转的星星,伪装为“粒子发射源”。
实验步骤
通过GLFW与GLAD构建自定义渲染管线,Shader文件的编译参考了LearnOpenGL的Shader
类,纹理图片的读取使用stb_image
库。
由于主要效果于片元着色器实现,主函数仅需一个绘制覆盖窗口的四边形,并将纹理坐标、时间与指定纹理传入着色器即可。
实验环境
Window 10. Visual Studio 2022.
依赖库:GLFW,GLAD,stb_image
渲染全屏quad
这里贴一下全屏方块的代码,方便以后ctrlC&ctrlV~
unsigned int quadVAO = 0;
unsigned int quadVBO;
void renderQuad()
{
if (quadVAO == 0)
{
float quadVertices[] = {
// positions // texture Coords
-1.0f, 1.0f, 0.0f, 0.0f, 1.0f,
-1.0f, -1.0f, 0.0f, 0.0f, 0.0f,
1.0f, 1.0f, 0.0f, 1.0f, 1.0f,
1.0f, -1.0f, 0.0f, 1.0f, 0.0f,
};
// setup plane VAO
glGenVertexArrays(1, &quadVAO);
glGenBuffers(1, &quadVBO);
glBindVertexArray(quadVAO);
glBindBuffer(GL_ARRAY_BUFFER, quadVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(quadVertices), &quadVertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));
}
glBindVertexArray(quadVAO);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glBindVertexArray(0);
}
以及顶点着色器:
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aTexCoords;
out vec2 TexCoords;
void main()
{
TexCoords = aTexCoords;
gl_Position = vec4(aPos, 1.0);
}
片元着色器:
#version 330 core
out vec4 FragColor;
in vec2 TexCoords;
void main()
{
FragColor = vec4(color, 1.0);
}
ODOS!
Shadertoy版本没有材质,用了iq的一个十字星的SDF。然后稍微改了一下着色的规则!下班!