쉐이더
오로라 그라디언트
청록, 보라, 분홍이 천천히 번지는 애니메이션 채우기 쉐이더
- #shader
- #gradient
- #aurora
- #fill
- #animated
1280px
설치
이 항목은 아직 설치할 수 없어요. 소스를 참고해 주세요. 설치할 수 없는 항목 알아보기
AI 프롬프트
Create a WebGPU fill shader that blends three sine waves into a teal/violet/pink gradient, driven by frame.time and two sliders: speed and intensity.
demo.tsxdemo.tsxdemo.tsxtsx
"use client";
import { ShaderCanvas } from "@devs7udio/demo-kit/shader";
import * as shader from "./main.web";
export default function Demo() {
return <ShaderCanvas module={shader} className="h-screen w-full" />;
}
main.tsmain.tsmain.tsts
import { defineProperties } from "figma:shaders"
export default function Fill() {}
export function setup(device, frame) {
frame.state.module = device.createShaderModule({
code: `
diagnostic(off,derivative_uniformity);
struct Uniforms { time: f32, speed: f32, intensity: f32, aspect: f32 }
@group(0) @binding(0) var<uniform> u: Uniforms;
struct VSOut { @builtin(position) pos: vec4f, @location(0) uv: vec2f }
@vertex
fn vs_main(@location(0) position: vec2f, @location(1) uv: vec2f) -> VSOut {
var o: VSOut;
o.pos = vec4f(position, 0.0, 1.0);
o.uv = uv;
return o;
}
@fragment
fn fs_main(frag: VSOut) -> @location(0) vec4f {
let t = u.time * u.speed;
var p = frag.uv;
p.x = p.x * u.aspect;
let w1 = sin(p.x * 3.0 + t * 0.9) * 0.5 + 0.5;
let w2 = sin(p.y * 4.0 - t * 0.7 + p.x * 1.5) * 0.5 + 0.5;
let w3 = sin((p.x + p.y) * 2.5 + t * 1.3) * 0.5 + 0.5;
let a = vec3f(0.10, 0.85, 0.65);
let b = vec3f(0.45, 0.35, 0.95);
let c = vec3f(0.95, 0.40, 0.75);
var col = mix(a, b, w1);
col = mix(col, c, w2 * 0.6);
let glow = (w3 * 0.5 + 0.25) * u.intensity;
col = col * (0.35 + glow);
return vec4f(col, 1.0);
}`,
})
frame.state.quad = device.createBuffer({
size: 6 * 4 * 4,
usage: GPUBufferUsage.VERTEX,
mappedAtCreation: true,
})
new Float32Array(frame.state.quad.getMappedRange()).set([
-1, -1, 0, 1, 1, -1, 1, 1, -1, 1, 0, 0,
-1, 1, 0, 0, 1, -1, 1, 1, 1, 1, 1, 0,
])
frame.state.quad.unmap()
frame.state.uniforms = device.createBuffer({
size: 16,
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
})
}
export function render(device, frame) {
const state = frame.state
const output = frame.output
if (state.pipelineFormat !== output.format) {
state.pipeline = device.createRenderPipeline({
layout: "auto",
vertex: {
module: state.module,
entryPoint: "vs_main",
buffers: [{
arrayStride: 16,
attributes: [
{ shaderLocation: 0, format: "float32x2", offset: 0 },
{ shaderLocation: 1, format: "float32x2", offset: 8 },
],
}],
},
fragment: {
module: state.module,
entryPoint: "fs_main",
targets: [{ format: output.format }],
},
primitive: { topology: "triangle-list" },
})
state.bindGroup = device.createBindGroup({
layout: state.pipeline.getBindGroupLayout(0),
entries: [{ binding: 0, resource: { buffer: state.uniforms } }],
})
state.pipelineFormat = output.format
}
device.queue.writeBuffer(
state.uniforms,
0,
new Float32Array([
Number(frame.time) * 0.001,
frame.params.speed,
frame.params.intensity,
output.width / output.height,
]),
)
const encoder = device.createCommandEncoder()
const pass = encoder.beginRenderPass({
colorAttachments: [{
view: output.createView(),
loadOp: "clear",
storeOp: "store",
clearValue: { r: 0, g: 0, b: 0, a: 0 },
}],
})
pass.setPipeline(state.pipeline)
pass.setBindGroup(0, state.bindGroup)
pass.setVertexBuffer(0, state.quad)
pass.draw(6)
pass.end()
device.queue.submit([encoder.finish()])
}
defineProperties(Fill, {
speed: { type: "number", control: "slider", min: 0, max: 3, step: 0.01, defaultValue: 1 },
intensity: { type: "number", control: "slider", min: 0, max: 2, step: 0.01, defaultValue: 1 },
})