⚕️ 主页: gis分享者
⚕️ 感谢各位大佬 点赞 收藏⭐ 留言 加关注✅!
⚕️ 收录于专栏:threejs gis工程师
本文详细介绍如何基于threejs在三维场景中使用自定义GLSL 着色器,生成艺术作品,亲测可用。希望能帮助到您。一起学习,加油!加油!
GLSL(OpenGL Shading Language)是OpenGL的核心编程语言,用于编写图形渲染管线中可定制的计算逻辑。其核心设计目标是通过GPU并行计算实现高效的图形处理,支持从基础几何变换到复杂物理模拟的多样化需求。
顶点着色器(Vertex Shader)
片段着色器(Fragment Shader)
计算着色器(Compute Shader,高级)
渲染管线流程
数据流动
语法特性
硬件加速
灵活性
游戏开发
数据可视化
艺术创作
教育与研究
顶点着色器(传递法线与世界坐标):
#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光照):
#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 着色器定义THREE.ShaderMaterial材质material,定义THREE.PlaneGeometry二维平面使用material材质生成艺术作品。具体代码参考代码样例。可以直接运行。
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>glsl着色器,生成艺术作品title>
<style>
body {
margin: 0;
overflow: hidden;
background-color: #111;
}
canvas {
display: block;
}
style>
head>
<body>
<div id="container">div>
<script type="module">
import * as THREE from 'https://cdn.jsdelivr.net/npm/[email protected]/build/three.module.js';
const fragmentShaderCode = `
precision highp float;
uniform vec2 u_resolution;
uniform float u_time;
varying vec2 vUv;
#define EPSILON 1e-6
#define PI 3.14159265359
#define ITERATIONS 18.0
mat2 rotate2d(float angle){
return mat2(cos(angle), -sin(angle),
sin(angle), cos(angle));
}
void main() {
vec2 uv = (vUv * 2.0 - 1.0) * (u_resolution / max(u_resolution.x, u_resolution.y));
vec2 u = uv * 0.25;
vec4 o = vec4(0.5, 1.0, 1.5, 0.0);
vec4 z = o;
vec2 v_internal = vec2(0.0);
float a = 0.6;
float t = u_time * 0.8;
for (float i = 0.0; i < ITERATIONS; i++)
{
float u_dot = dot(u, u);
float denom_u = 0.6 - u_dot;
denom_u += sign(denom_u) * EPSILON;
vec2 sin_arg = (1.4 * u / denom_u) - (7.0 * u.yx * cos(t*0.2)) + t * 1.1 + v_internal * 0.3;
vec2 length_arg = (1.0 + i * 0.1 + a * 0.2) * sin(sin_arg);
float len = length(length_arg);
float safe_len_divisor = max(len, EPSILON);
o += (1.0 + sin(z * 0.9 + t * 1.2 + i * 0.1)) / safe_len_divisor * (1.0 + i*0.02);
v_internal = 0.9 * v_internal + 0.15 * sin(t * 1.5 + u * 4.0 - o.xy * 0.2);
v_internal = clamp(v_internal, -1.0, 1.0);
a += 0.035;
float angle = i * 0.1 + t * 0.05 + a * 0.2;
mat2 rot_mat = rotate2d(angle);
u *= rot_mat;
float o_dot = dot(o.xyz, o.xyz);
float feedback_scale = 0.5 + 0.5 * sin(o_dot * 0.02 + t * 0.3);
u += sin(60.0 * dot(u,u) * cos(80.0 * u.yx + t * 1.2)) / 2.5e2
+ 0.15 * a * u * feedback_scale
+ cos(o.xy * 0.5 + t * 1.1 + v_internal * 0.8) / 3.5e2;
u += rotate2d(v_internal.x * 0.01) * vec2(0.0001, 0.0);
}
vec3 base_color = 0.5 + 0.5 * cos(o.xyz * 0.8 + t * 0.15 + vec3(0.0, PI * 0.66, PI * 1.33));
vec2 detail_coord = u * 5.0 + v_internal * 1.0;
float detail_pattern = smoothstep(0.3, 0.7, fract(detail_coord.x * cos(t*0.1) + detail_coord.y * sin(t*0.1)));
vec3 detail_color = vec3(detail_pattern * 0.8 + 0.2);
float mix_factor = clamp(length(o.xyz) * 0.1 - 0.1, 0.0, 1.0);
vec3 final_color = mix(base_color, detail_color * base_color, mix_factor);
final_color.rg += u.xy * 0.05;
float dist_from_center = length(vUv - 0.5);
final_color *= pow(1.0 - dist_from_center * 1.2, 2.0);
gl_FragColor = vec4(clamp(final_color, 0.0, 1.0), 1.0);
}
`;
const vertexShaderCode = `
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = vec4( position, 1.0 );
}
`;
let scene, camera, renderer, mesh, material, clock;
let container;
const uniforms = {
u_time: { value: 0.0 },
u_resolution: { value: new THREE.Vector2() }
};
function init() {
container = document.getElementById('container');
clock = new THREE.Clock();
renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(window.devicePixelRatio);
container.appendChild(renderer.domElement);
scene = new THREE.Scene();
camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1);
const geometry = new THREE.PlaneGeometry(2, 2);
material = new THREE.ShaderMaterial({
uniforms: uniforms,
vertexShader: vertexShaderCode,
fragmentShader: fragmentShaderCode
});
mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
uniforms.u_resolution.value.x = window.innerWidth;
uniforms.u_resolution.value.y = window.innerHeight;
window.addEventListener('resize', onWindowResize);
renderer.setAnimationLoop(animate);
console.log("Three.js setup complete. Starting animation loop.");
}
function onWindowResize() {
renderer.setSize(window.innerWidth, window.innerHeight);
uniforms.u_resolution.value.x = window.innerWidth;
uniforms.u_resolution.value.y = window.innerHeight;
material.uniforms.u_resolution.value.set(window.innerWidth, window.innerHeight);
}
function animate() {
uniforms.u_time.value = clock.getElapsedTime();
renderer.render(scene, camera);
}
init();
script>
body>