稀土掘金技术社区 07月09日 09:42
学习一个马斯克转发的Grok效果(glsl)
index_new5.html
../../../zaker_core/zaker_tpl_static/wap/tpl_guoji1.html

 

本文分享了GLSL(OpenGL Shading Language)的基础知识和实践,通过几个简单的例子,包括红色填充、渐变、径向渐变和发光圆环,逐步引导读者理解GLSL的绘制原理和坐标系统。文章深入浅出地讲解了gl_FragColor、gl_FragCoord、u_resolution等关键概念,并详细解释了如何利用数学计算实现各种视觉效果,最终实现了被马斯克转发的四行代码效果。文章适合GLSL新手入门,提供了清晰的代码示例和解释,帮助读者快速上手。

💻 **GLSL核心概念:** GLSL是一种用于编写着色器的语言,其核心在于每个像素的独立计算和同时渲染。与传统2D绘图的顺序绘制不同,GLSL利用数学计算和函数式编程来定义每个像素的颜色。

🟥 **gl_FragColor与颜色:** gl_FragColor是GLSL中用于设置像素颜色的内置变量。通过设置vec4类型的gl_FragColor,可以定义红、绿、蓝和透明度(RGBA),从而控制像素的最终显示颜色。

📏 **坐标系统与归一化:** GLSL使用坐标系统,其中gl_FragCoord提供了像素的绝对坐标。通过将像素坐标归一化到0.0到1.0的范围,可以方便地实现渐变等效果,例如通过pos.x控制水平渐变。

⭕ **径向渐变与发光圆环:** 通过计算像素点到中心的距离,并结合abs和除法等数学运算,可以实现径向渐变和发光圆环效果。这种方法展示了GLSL中利用数学实现复杂视觉效果的能力。

✨ **四行代码的奥秘:** 通过对角线的数学定义(x = y)和反转亮度,最终实现被马斯克转发的四行代码效果,展示了GLSL的强大表现力。

原创 苏武南飞 2025-07-09 08:31 重庆

写在开头最近在学习glsl,在网上找相关开源代码效果学习时看到了这个被「马斯克」转发,并且只用四行就完成的效果

写在开头最近在学习

glsl

,在网上找相关开源代码效果学习时看到了这个被「马斯克」转发,并且只用四行就完成的效果!自我学习之后分享出来,因为本人也是一个

glsl

新手,有错误的地方欢迎大家斧正! 本篇文章只会涉及到「片段着色器」,下面代码如果不特殊提醒均为「片段着色器」
效果原作者 Xor

效果欣赏!🔑入门

glsl

首先我们应该了解到之前我们使用的

canvas 2D

绘制和

glsl

绘制的一些差异性:
Canvas 2D 的绘制方式:

类似传统绘画,按顺序一笔一笔画

从左上角 (0,0) 开始,y 轴向下

命令式编程:moveTo(), lineTo(), fill()

时间顺序执行,后画的覆盖先画的

GLSL 的绘制方式:

每个像素点同时计算,高度并行

坐标系通常以中心为 (0,0),y 轴向上

函数式编程:给定坐标,返回颜色

所有像素 "瞬间" 同时渲染

这是学习

glsl

初期相当让人不适应的一点,

glsl

非常类似中国古代的活字印刷[1]技术,所有像素点同时渲染,几何形状需要依靠数学计算来排列组合!下面举得例子中都要谨记这个概念「瞬间 / 同时渲染」

void main() {

    gl_FragColor = vec4(1.00.00.01.0);

}

gl_FragColor 是 GLSL 片元着色器 (Fragment Shader) 中的一个内置输出变量,它决定了当前像素最终显示的颜色。

gl_FragColor 的核心概念本质:它是每个像素的 "最终答案" - 告诉 GPU 这个像素应该显示什么颜色。

vec4 的四个分量含义

gl_FragColor = vec4(r, g, b, a);

//                  ↑  ↑  ↑  ↑

//                  红 绿 蓝 透明度

r (Red):红色分量,范围 0.0-1.0

g (Green):绿色分量,范围 0.0-1.0

b (Blue):蓝色分量,范围 0.0-1.0

a (Alpha):透明度,0.0 完全透明,1.0 完全不透明

void main() {

    gl_FragColor = vec4(1.00.00.01.0);

}

因为 r=1.0 所以我们这个代码的效果是一个红色的填充。

工作原理GPU 为屏幕上的每个像素都调用一次这个片元着色器

每次调用时,main() 函数执行

gl_FragColor 被设置为红色

所有像素都得到相同的红色值

最终屏幕显示为一片红色

就像每个像素都调用了 main 方法得到的命令是都给我渲染成_红色_,结果就是整个画布展示为红色。

更多例子

void main() {

    vec2 pos = gl_FragCoord.xy / u_resolution.xy;

    gl_FragColor = vec4(pos.x0.00.01.0);

}

gl_FragCoord - 像素的绝对坐标

gl_FragCoord

 是 GLSL 的内置变量,表示当前像素在屏幕上的绝对位置(以像素为单位)。

// 假设画布是 800x600 像素,不考虑采样精度

// gl_FragCoord.xy 的取值范围是:

// x: 0 到 800

// y: 0 到 600

u_resolution - 画布的总尺寸u_resolution 是一个变量,表示画布的宽高。

归一化坐标的计算

vec2 pos = gl_FragCoord.xy / u_resolution.xy;

这行代码将绝对像素坐标转换为归一化坐标(0.0 到 1.0):

举例:画布800x600,当前像素在(400, 300)

pos.x = 400 / 800 = 0.5  (画布中央)

pos.y = 300 / 600 = 0.5  (画布中央)

渐变效果的实现

gl_FragColor = vec4(pos.x0.00.01.0);

//                    ↑

//                 红色分量由x坐标决定

最左边:pos.x = 0.0 → 黑色 (0, 0, 0, 1)

最右边:pos.x = 1.0 → 纯红 (1, 0, 0, 1)

中间:pos.x = 0.5 → 半红 (0.5, 0, 0, 1)

这个效果是纵向渐变,我们举一反三实现一个垂直渐变

void main() {

    vec2 pos = gl_FragCoord.xy / u_resolution.xy;

    gl_FragColor = vec4(pos.y0.00.01.0);

}

从这两个渐变效果我们就能了解到

glsl

坐标系是「从左到右」 / 「从下到上」

0,1 - - - - -  1,1

|               |

|               |

0,0 - - - - - 1,0

径向渐变线性渐变我们已经掌握了接下来我们来挑战一下径向渐变!

void main() {

    vec2 pos = gl_FragCoord.xy / u_resolution.xy;

    pos = pos * 2.0 - 1.0;

    float dist = length(pos);

    gl_FragColor = vec4(dist, dist, dist, 1.0);

}

pos = pos * 2.0 - 1.0;

在于把中心点变换到

[0,0]

vec2 pos = gl_FragCoord.xy / u_resolution.xy;

 是归一化坐标也就是坐标范围此时为

[0,1]

此时坐标中心点是

[0.5,0.5]

, 经过 

pos * 2.0 - 1.0;

之后我们的坐标范围变成了

[-1,1]

,此时的坐标中心点就是

[0,0]

啦。

length()

 是 GLSL 中的内置数学函数,用来计算向量的欧几里得长度[2], 也就是我们熟知的勾股定理,对于对于 2D 向量

vec2(x, y)

来说!

length(vec2(xy)) = sqrt(x*x + y*y)

这就是我们为什么需要把作用中心点转换到

[0,0]

的原因!我们的

dist

的值靠近中心点就约等于 0,越靠近外层就约等于 1发光的圆环

void main() {

    vec2 pos = gl_FragCoord.xy / u_resolution.xy;

    pos = pos * 2.0 - 1.0;

    float dist = length(pos);// 计算与圆环的距离

    float ring = abs(dist - 0.5);// 距离越近越亮

    float brightness = 0.1 / ring;

    gl_FragColor = vec4(brightness, brightness, brightness, 1.0);

}

OK Fine,现在我们提升一点难度,开始我最不喜欢的数学!首先是

dist

可以产生一个径向渐变中心是黑色往外扩散白色,但是我们的目的是一个圆环,也就是中心要是 

黑色->白色->黑色

,通过

float ring = abs(dist - 0.5)

就可以产生一个明显的分层效果!

假设:

dist = 0; ring = 0.5;

dist = 0.5; ring = 0;

dist = 1; ring = 0.5; 

float brightness = 0.1 / ring;

的作用就是提亮!

ring 值

brightness = 0.1 / ring

效果

0.5

0.1 / 0.5 = 0.2

暗灰色

0.2

0.1 / 0.2 = 0.5

中等亮度

0.1

0.1 / 0.1 = 1.0

比较亮

0.05

0.1 / 0.05 = 2.0

很亮!

0.01

0.1 / 0.01 = 10.0

超亮!!

0.001

0.1 / 0.001 = 100.0

极亮!!!

数学规律:当分母越接近 0,结果越接近无穷大! 这就是为什么圆环会发光 - 在半径 0.5 的位置,分母最小,亮度最大!

完成 Grok 效果看完上述几个例子后,我们已经可以完成一个发亮的圆环,现在距离最终的效果只差一个对角线! 

🎯 对角线的数学定义对角线是所有满足 x = y 的点

在这些点上:p.x - p.y = x - x = 0

数值分布

左上角:p.x < p.y,所以 p.x - p.y < 0(负值)

右下角:p.x > p.y,所以 p.x - p.y > 0(正值)

对角线:p.x = p.y,所以 p.x - p.y = 0(零值)

除法放大

0.01 / (p.x - p.y)

当 p.x - p.y 接近 0 时(对角线附近)

除法结果趋向无穷大 → 产生极亮的线

我们加上这个对角线后为了让对角线在圆环中不发光需要反转亮度,想想我们之前的数学规律

当分母越接近0,结果越接近无穷大!

 , 反过来如果分母越大结果自然也就越小!! 结语

glsl

实在是太有意思了!同时也太难啦!!!参考资料Xor:www.shadertoy.com/user/Xor

The Book of Shaders:thebookofshaders.com/

AI编程资讯AI Coding专区指南:https://aicoding.juejin.cn/aicoding

""~

阅读原文

跳转微信打开

Fish AI Reader

Fish AI Reader

AI辅助创作,多种专业模板,深度分析,高质量内容生成。从观点提取到深度思考,FishAI为您提供全方位的创作支持。新版本引入自定义参数,让您的创作更加个性化和精准。

FishAI

FishAI

鱼阅,AI 时代的下一个智能信息助手,助你摆脱信息焦虑

联系邮箱 441953276@qq.com

相关标签

GLSL 着色器 图形编程 ShaderToy 马斯克
相关文章