👨⚕️ 主页: gis分享者
👨⚕️ 感谢各位大佬 点赞👍 收藏⭐ 留言📝 加关注✅!
文章目录
- 一、🍀前言
-
- [1.1 ☘️GLSL着色器](#1.1 ☘️GLSL着色器)
-
- [1.1.1 ☘️着色器类型](#1.1.1 ☘️着色器类型)
- [1.1.2 ☘️工作原理](#1.1.2 ☘️工作原理)
- [1.1.3 ☘️核心特点](#1.1.3 ☘️核心特点)
- [1.1.4 ☘️应用场景](#1.1.4 ☘️应用场景)
- [1.1.5 ☘️实战示例](#1.1.5 ☘️实战示例)
- [二、🍀使用自定义GLSL 着色器,实现抽象艺术特效](#二、🍀使用自定义GLSL 着色器,实现抽象艺术特效)
-
- [1. ☘️实现思路](#1. ☘️实现思路)
- [2. ☘️代码样例](#2. ☘️代码样例)
一、🍀前言
本文详细介绍如何基于threejs在三维场景中使用自定义GLSL 着色器,实现抽象艺术特效。亲测可用。希望能帮助到您。一起学习,加油!加油!
1.1 ☘️GLSL着色器
GLSL(OpenGL Shading Language)是OpenGL的核心编程语言,用于编写图形渲染管线中可定制的计算逻辑。其核心设计目标是通过GPU并行计算实现高效的图形处理,支持从基础几何变换到复杂物理模拟的多样化需求。
1.1.1 ☘️着色器类型
顶点着色器(Vertex Shader)
- 功能:处理每个顶点的坐标变换(如模型视图投影矩阵变换)、法线计算及顶点颜色传递。
- 输出:裁剪空间坐标gl_Position,供后续光栅化阶段使用。
片段着色器(Fragment Shader)
- 功能:计算每个像素的最终颜色,支持纹理采样、光照模型(如Phong、PBR)及后处理效果(如模糊、景深)。
- 输出:像素颜色gl_FragColor或gl_FragColor(RGBA格式)。
计算着色器(Compute Shader,高级)
- 功能:执行通用并行计算任务(如物理模拟、图像处理),不直接绑定渲染管线。
- 特点:通过工作组(Work Group)实现高效数据并行处理。
1.1.2 ☘️工作原理
渲染管线流程
- 顶点处理:CPU提交顶点数据(位置、颜色、纹理坐标),GPU并行执行顶点着色器处理每个顶点。
- 光栅化:将顶点数据转换为像素片段,生成片段着色器输入。
- 片段处理:GPU并行执行片段着色器计算每个像素颜色。
- 输出合并:将片段颜色与帧缓冲区混合,生成最终图像。
数据流动
- 顶点属性:通过glVertexAttribPointer传递位置、颜色等数据,索引由layout(location=N)指定。
- Uniform变量:CPU通过glGetUniformLocation传递常量数据(如变换矩阵、时间),在渲染循环中更新。
- 内置变量: gl_Position(顶点着色器输出):裁剪空间坐标。 gl_FragCoord(片段着色器输入):当前像素的窗口坐标。
gl_FrontFacing(片段着色器输入):判断像素是否属于正面三角形。
1.1.3 ☘️核心特点
语法特性
- C语言变体:支持条件语句、循环、函数等结构,天然适配图形算法。
- 向量/矩阵运算:内置vec2/vec3/vec4及mat2/mat3/mat4类型,支持点乘、叉乘等操作。
- 精度限定符:如precision mediump float,控制计算精度与性能平衡。
硬件加速
- 并行计算:GPU数千个核心并行执行着色器代码,适合处理大规模数据(如粒子系统、体素渲染)。
- 内存模型:支持常量内存(Uniform)、纹理内存(Sampler)及共享内存(计算着色器),优化数据访问效率。
灵活性
- 可编程管线:完全替代固定渲染管线,支持自定义光照、阴影、后处理效果。
- 跨平台兼容性:OpenGL ES(移动端)与WebGL(Web)均支持GLSL,代码可移植性强。
1.1.4 ☘️应用场景
游戏开发
- 实时渲染:实现PBR材质、动态阴影、屏幕空间反射。
- 特效系统:粒子火焰、流体模拟、布料物理。
- 性能优化:通过计算着色器加速AI计算、碰撞检测。
数据可视化
- 科学计算:将多维数据映射为颜色/高度图(如气象数据、流场可视化)。
- 信息图表:动态生成3D柱状图、热力图,增强数据表现力。
艺术创作
- 程序化生成:使用噪声函数(如Perlin、Simplex)生成地形、纹理。
- 交互式装置:结合传感器数据实时修改着色器参数,创造动态艺术作品。
教育与研究
- 算法实验:实时调试光线追踪、路径追踪算法。
- 教学工具:可视化线性代数运算(如矩阵变换、向量投影)。
1.1.5 ☘️实战示例
顶点着色器(传递法线与世界坐标):
javascript
#version 330 core
layout(location=0) in vec3 aPos;
layout(location=1) in vec3 aNormal;
out vec3 FragPos;
out vec3 Normal;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main() {
FragPos = vec3(model * vec4(aPos, 1.0));
Normal = mat3(transpose(inverse(model))) * aNormal; // 模型空间到世界空间的法线变换
gl_Position = projection * view * vec4(FragPos, 1.0);
}
片段着色器(实现Blinn-Phong光照):
javascript
#version 330 core
in vec3 FragPos;
in vec3 Normal;
out vec4 FragColor;
uniform vec3 lightPos;
uniform vec3 viewPos;
uniform vec3 lightColor;
uniform vec3 objectColor;
void main() {
// 环境光
vec3 ambient = 0.1 * lightColor;
// 漫反射
vec3 norm = normalize(Normal);
vec3 lightDir = normalize(lightPos - FragPos);
float diff = max(dot(norm, lightDir), 0.0);
vec3 diffuse = diff * lightColor;
// 镜面反射
vec3 viewDir = normalize(viewPos - FragPos);
vec3 reflectDir = reflect(-lightDir, norm);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32);
vec3 specular = 0.5 * spec * lightColor;
// 最终颜色
vec3 result = (ambient + diffuse + specular) * objectColor;
FragColor = vec4(result, 1.0);
}
二、🍀使用自定义GLSL 着色器,实现抽象艺术特效
1. ☘️实现思路
使用自定义GLSL 着色器定义THREE.ShaderMaterial材质material,定义THREE.PlaneGeometry作为颜色渲染平面,实现抽象艺术特效。具体代码参考代码样例。可以直接运行。
2. ☘️代码样例
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>glsl颜色_抽象艺术作品</title>
<style>
body {
margin: 0;
overflow: hidden;
background: #020010;
}
canvas {
display: block;
width: 100vw;
height: 100vh;
}
</style>
</head>
<body>
</body>
<script type="importmap">
{
"imports": {
"three": "https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.module.js"
}
}
</script>
<script type="module">
import * as THREE from 'three';
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x000000);
const camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
const material = new THREE.ShaderMaterial({
uniforms: {
time: { value: 0 },
resolution: { value: new THREE.Vector2(window.innerWidth, window.innerHeight) },
mouse: { value: new THREE.Vector2(0.5, 0.5) }
},
vertexShader: `
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
fragmentShader: `
uniform float time;
uniform vec2 resolution;
uniform vec2 mouse;
varying vec2 vUv;
mat2 rot(float a) {
float s = sin(a);
float c = cos(a);
return mat2(c, -s, s, c);
}
void main() {
vec2 FC = gl_FragCoord.xy;
vec2 r = resolution;
float t = time;
vec4 o = vec4(0.0);
vec2 uv = (FC * 2.0 - r.xy) / r.y;
vec3 rayDir = normalize(vec3(uv, 1.0));
for(float i = 0.0; i < 20.0; i++) {
float z = i * 0.5 + 0.1;
vec3 p = z * rayDir;
vec2 mouseOffset = (mouse - 0.5) * 1.0;
p.xz *= rot(t * 0.2 + mouseOffset.x);
p.yz *= rot(t * 0.15 + mouseOffset.y);
vec3 a = p;
float d;
for(d = 1.0; d < 4.0; d += 1.0) {
a -= sin(a * d + t + i*0.1).yzx / (d * 0.8);
}
vec3 q = abs(a);
float mx = max(max(q.x, q.y), q.z);
float s = a.z + a.y - t;
d = abs(2.0 - mx) + abs(cos(s)) / 7.0;
float z_col = z + d;
vec4 colorPhase = vec4(0.0, 2.0 + mouse.y * 2.0, 4.0 - mouse.y * 2.0, 0.0);
o += (cos(s - z_col + colorPhase) + 1.0) / (d*0.5 + 0.01);
}
o = o / 20.0;
o = o / (1.0 + o);
o = smoothstep(0.15, 0.85, o);
gl_FragColor = o;
gl_FragColor.a = 1.0;
}
`
});
const geometry = new THREE.PlaneGeometry(2, 2);
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
window.addEventListener('resize', () => {
const width = window.innerWidth;
const height = window.innerHeight;
renderer.setSize(width, height);
material.uniforms.resolution.value.set(width, height);
});
window.addEventListener('mousemove', (event) => {
material.uniforms.mouse.value.x = event.clientX / window.innerWidth;
material.uniforms.mouse.value.y = 1.0 - (event.clientY / window.innerHeight);
});
function animate() {
requestAnimationFrame(animate);
material.uniforms.time.value += 0.02;
renderer.render(scene, camera);
}
window.onload = function () {
animate();
}
</script>
</html>
效果如下

参考:源码