跳到正文
cccskills
免费在 GitHub 公开

webgl-laser

Create a fixed, full-screen WebGL laser background with a thin white core, brand-colored halo, and soft smoke. Adjust beam placement, glow, fog, and rendering cost while keeping foreground content readable and interactive.

中文简介正在准备中。当前显示英语简介。

原文查看原文说明

Create a fixed full-screen WebGL laser background effect with a thin white-hot vertical core, restrained brand-colored halo, and soft smoky fog around the beam. Use only for laser background effects, not full page layout, copy, generic hero scenes, particles, or unrelated motion systems.

查看安装方法

适用场景

  • Adding a thin laser behind page content
  • Matching laser glow and smoke to brand colors
  • Adjusting beam position and smoke density
  • Tuning rendering cost and reduced-motion behavior

包含的文件(10)

  • SKILL.md9.2 KB
  • demo/assets/page-01-2f563338-39fa-47ea-9761-658d4f3f84db-1600w.jpg292.0 KB
  • demo/assets/page-02-4f5668c5-fc4a-44e0-bc5e-a664189d3c31-1600w.jpg212.3 KB
  • demo/assets/page-03-77415a2e-dcbc-4748-a29d-fced4821881a-1600w.jpg354.2 KB
  • demo/assets/page-04-c92852bb-a510-405a-85ab-ffa0fde136a4-1600w.jpg209.1 KB
  • demo/assets/source-preview.jpg72.1 KB
  • demo/index.html21.4 KB
  • demo/preview.jpg54.3 KB
  • demo/PROMPT.md3.3 KB
  • demo/source.json3.2 KB

SKILL.md(原文)

安装前可以查看交给 AI 代理的指令内容。

WebGL Laser

Scope

  • Apply only to the laser background effect.
  • Use a fixed full-screen canvas behind the DOM.
  • Set pointer-events: none on the canvas.
  • Keep page content in a higher stacking context.
  • Match the halo and smoke to the page's primary or strongest accent color.

Visual Target

  • Thin vertical beam: crisp white-hot inner core, narrow colored halo.
  • Atmospheric smoke: soft cloudy breakup concentrated around the beam.
  • Dark cinematic field: restrained, brand-colored, and readable behind content.
  • Slow pulse: glow breathes gently; no aggressive flicker or color cycling.
  • Light blade feel: narrow and precise, never a thick neon pillar.

Layering

<canvas class="laser-canvas" data-webgl-laser></canvas>
<main class="page-content">
  ...
</main>
.laser-canvas {
  position: fixed;
  inset: 0;
  z-index: 0;
  width: 100vw;
  height: 100vh;
  pointer-events: none;
}

.page-content {
  position: relative;
  z-index: 1;
}

Brand Color

Use the product accent as the source color. The shader keeps the core near white and derives the halo/smoke from this color.

function hexToRgb01(hex) {
  const clean = hex.replace("#", "").trim();
  const value = clean.length === 3
    ? clean.split("").map((char) => char + char).join("")
    : clean;

  return [
    parseInt(value.slice(0, 2), 16) / 255,
    parseInt(value.slice(2, 4), 16) / 255,
    parseInt(value.slice(4, 6), 16) / 255,
  ];
}

const accent = getComputedStyle(document.documentElement)
  .getPropertyValue("--brand-accent")
  .trim() || "#ff4d8d";

Raw WebGL Setup

Prefer raw WebGL with a full-screen quad unless the active file already uses another renderer.

const laserVertexShader = `
attribute vec2 a_position;
varying vec2 v_uv;

void main() {
  v_uv = a_position * 0.5 + 0.5;
  gl_Position = vec4(a_position, 0.0, 1.0);
}
`;

const laserFragmentShader = `
precision highp float;

uniform vec2 u_resolution;
uniform float u_time;
uniform vec3 u_color;
uniform float u_xOffset;
uniform float u_coreWidth;
uniform float u_glowWidth;
uniform float u_smokeDensity;

varying vec2 v_uv;

float hash(vec2 p) {
  p = fract(p * vec2(123.34, 456.21));
  p += dot(p, p + 45.32);
  return fract(p.x * p.y);
}

float noise(vec2 p) {
  vec2 i = floor(p);
  vec2 f = fract(p);
  vec2 u = f * f * (3.0 - 2.0 * f);

  float a = hash(i);
  float b = hash(i + vec2(1.0, 0.0));
  float c = hash(i + vec2(0.0, 1.0));
  float d = hash(i + vec2(1.0, 1.0));

  return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);
}

float fbm(vec2 p) {
  float value = 0.0;
  float amplitude = 0.5;

  for (int i = 0; i < 5; i++) {
    value += amplitude * noise(p);
    p *= 2.02;
    amplitude *= 0.5;
  }

  return value;
}

void main() {
  vec2 aspect = vec2(u_resolution.x / u_resolution.y, 1.0);
  vec2 p = (v_uv - 0.5) * aspect;
  float x = p.x - u_xOffset;
  float distanceToBeam = abs(x);

  float core = exp(-pow(distanceToBeam / u_coreWidth, 2.0));
  float glow = exp(-pow(distanceToBeam / u_glowWidth, 1.45));
  float scatter = exp(-pow(distanceToBeam / (u_glowWidth * 5.5), 1.25));
  float pulse = 0.9 + 0.1 * sin(u_time * 1.15);

  vec2 fogUv = p * 3.1 + vec2(0.0, -u_time * 0.035);
  fogUv.x += sin(p.y * 3.5 + u_time * 0.11) * 0.14;
  float fogBase = fbm(fogUv);
  float fogFine = fbm(p * 8.0 + vec2(sin(u_time * 0.07) * 0.35, u_time * 0.05));
  float fog = smoothstep(0.30, 0.86, fogBase * 0.72 + fogFine * 0.28);
  float smoke = fog * scatter * u_smokeDensity;

  vec3 brand = clamp(u_color, 0.0, 1.0);
  vec3 haloColor = mix(brand, vec3(1.0), 0.16);
  vec3 smokeColor = mix(brand, vec3(0.55), 0.28) * 0.55;
  vec3 hotCore = vec3(1.0, 0.96, 0.90);

  vec3 color = vec3(0.006, 0.007, 0.010);
  color += smokeColor * smoke;
  color += haloColor * glow * 0.46 * pulse;
  color += hotCore * core * 1.35;

  float vignette = smoothstep(1.25, 0.18, length(p));
  color *= vignette;

  float alpha = clamp(smoke * 0.72 + glow * 0.68 + core, 0.0, 1.0);
  gl_FragColor = vec4(color, alpha);
}
`;

Initializer

Keep u_resolution synced on resize and animate through u_time.

function createShader(gl, type, source) {
  const shader = gl.createShader(type);
  gl.shaderSource(shader, source);
  gl.compileShader(shader);

  if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
    throw new Error(gl.getShaderInfoLog(shader) || "Shader compile failed");
  }

  return shader;
}

function createProgram(gl, vertexSource, fragmentSource) {
  const program = gl.createProgram();
  gl.attachShader(program, createShader(gl, gl.VERTEX_SHADER, vertexSource));
  gl.attachShader(program, createShader(gl, gl.FRAGMENT_SHADER, fragmentSource));
  gl.linkProgram(program);

  if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
    throw new Error(gl.getProgramInfoLog(program) || "Program link failed");
  }

  return program;
}

function initWebGLLaser(canvas, options = {}) {
  if (!canvas) return () => {};

  const gl = canvas.getContext("webgl", {
    alpha: true,
    antialias: false,
    premultipliedAlpha: false,
  });

  if (!gl) return () => {};

  const program = createProgram(gl, laserVertexShader, laserFragmentShader);
  const positionBuffer = gl.createBuffer();
  const positions = new Float32Array([
    -1, -1,
     1, -1,
    -1,  1,
    -1,  1,
     1, -1,
     1,  1,
  ]);

  gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
  gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW);
  gl.useProgram(program);

  const positionLocation = gl.getAttribLocation(program, "a_position");
  const uniforms = {
    resolution: gl.getUniformLocation(program, "u_resolution"),
    time: gl.getUniformLocation(program, "u_time"),
    color: gl.getUniformLocation(program, "u_color"),
    xOffset: gl.getUniformLocation(program, "u_xOffset"),
    coreWidth: gl.getUniformLocation(program, "u_coreWidth"),
    glowWidth: gl.getUniformLocation(program, "u_glowWidth"),
    smokeDensity: gl.getUniformLocation(program, "u_smokeDensity"),
  };

  gl.enableVertexAttribArray(positionLocation);
  gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
  gl.enable(gl.BLEND);
  gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);

  const color = options.color || hexToRgb01(accent);
  const reduceMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
  let width = 1;
  let height = 1;
  let rafId = 0;

  function resize() {
    const dpr = Math.min(window.devicePixelRatio || 1, options.maxDpr || 1.5);
    width = Math.max(1, window.innerWidth);
    height = Math.max(1, window.innerHeight);
    canvas.width = Math.floor(width * dpr);
    canvas.height = Math.floor(height * dpr);
    gl.viewport(0, 0, canvas.width, canvas.height);
  }

  function render(time = 0) {
    gl.useProgram(program);
    gl.uniform2f(uniforms.resolution, canvas.width, canvas.height);
    gl.uniform1f(uniforms.time, time * 0.001);
    gl.uniform3f(uniforms.color, color[0], color[1], color[2]);
    gl.uniform1f(uniforms.xOffset, options.xOffset || 0.0);
    gl.uniform1f(uniforms.coreWidth, options.coreWidth || 0.0045);
    gl.uniform1f(uniforms.glowWidth, options.glowWidth || 0.035);
    gl.uniform1f(uniforms.smokeDensity, options.smokeDensity || 0.52);

    gl.clearColor(0, 0, 0, 0);
    gl.clear(gl.COLOR_BUFFER_BIT);
    gl.drawArrays(gl.TRIANGLES, 0, 6);

    if (!reduceMotion) rafId = requestAnimationFrame(render);
  }

  function handleResize() {
    resize();
    render();
  }

  resize();
  render();
  window.addEventListener("resize", handleResize);

  return () => {
    cancelAnimationFrame(rafId);
    window.removeEventListener("resize", handleResize);
    gl.deleteBuffer(positionBuffer);
    gl.deleteProgram(program);
  };
}

const cleanupLaser = initWebGLLaser(document.querySelector("[data-webgl-laser]"), {
  color: hexToRgb01(accent),
  xOffset: 0.0,
  coreWidth: 0.0045,
  glowWidth: 0.035,
  smokeDensity: 0.52,
  maxDpr: 1.5,
});

Tuning Knobs

  • Beam position: adjust xOffset; keep it in aspect-correct centered UV space.
  • Beam thickness: tune coreWidth separately from glowWidth; keep the core extremely thin.
  • Color: derive color from the brand accent, then soften halo and smoke in shader.
  • Smoke density: tune smokeDensity, FBM scale, drift speed, scatter width, and edge falloff.
  • Performance: reduce FBM octaves or cap maxDpr before changing the visual structure.

Taste Rules

  • The hottest beam core stays near white.
  • The halo and fog use the design's primary or strongest accent color.
  • Smoke blooms near the beam and dissipates outward.
  • Pulse affects glow only; avoid rapid flicker.
  • Content readability wins over bloom, haze, or cinematic drama.

Avoid

  • Hardcoding blue when the design uses another primary color.
  • Making the beam thick enough to read as a glowing bar.
  • Generic full-screen fog that is not concentrated around the beam.
  • Turning the effect into a Three.js scene, particle explosion, or multicolor neon background.
  • Letting the canvas intercept pointer events.
  • Dense fog or extreme bloom that washes out foreground UI.

评价

还没有评价。欢迎分享你的使用感受。

评论

无需登录即可发表。欢迎分享使用感受、提问,或说明在哪些环境下无法运行。

还没有评论。来写第一条吧。

提交后会立即公开。请勿填写邮箱、电话等个人信息。URL 最多 2 个。

0 / 1000 字

此表单受 reCAPTCHA 保护,适用 Google 的隐私权政策和服务条款。

同一仓库的技能

Build woven fabric in Three.js with visible threads, realistic sheen, seams, and thick hems. The skill covers folds that preserve texture proportions, light passing through thin cloth, and variations in dye and wear.

中文简介正在准备中。当前显示英语简介。

  • Creating backlit curtains
  • Differentiating linen, silk, and wool
  • Adding hems and stitches for close-up views
MengTo/Skills68622026年10月18日 更新

Build falling leaves in Three.js and similar 3D scenes, with visible front, edge, and back views as they tumble and drift. Coordinate wind, scene depth, camera-aware recycling, and performance checks for a convincing autumn effect.

中文简介正在准备中。当前显示英语简介。

  • Adding falling autumn leaves to a 3D scene
  • Coordinating leaf tumble, sideways drift, and wind
  • Managing leaves around buildings and camera movement
MengTo/Skills68622026年10月18日 更新

Add coordinated seasonal transitions to an existing 3D scene. Blend foliage, lighting, sky, ground, snow, and particles from one shared state while preserving camera controls and interactions, so the same place remains recognizable throughout spring, summer, fall, and winter.

中文简介正在准备中。当前显示英语简介。

  • Add a four-season selector to an outdoor 3D scene
  • Show seasonal changes in an architectural walkthrough
  • Coordinate foliage, sunlight, sky, and ground appearance
MengTo/Skills68622026年10月18日 更新

Create or integrate detailed 3D models with smooth outlines and believable edges for close-up views. Prepare lower-detail versions for distant viewing, prioritize loading, and measure rendering costs in the actual scene.

中文简介正在准备中。当前显示英语简介。

  • Creating a detailed 3D model for close-up views
  • Replacing visibly faceted curves with smoother geometry
  • Preparing detailed architectural assets for rendering
MengTo/Skills68622026年10月18日 更新

Prepare detailed 3D materials that remain clear in close-up views. The skill guides texture resolution, physically based material maps, surface mapping, filtering, and progressive loading while checking download size and GPU memory use.

中文简介正在准备中。当前显示英语简介。

  • Prepare wood, stone, and fabric materials for close-up views
  • Fix texture seams, grain direction, and normal map orientation
  • Preserve floor and foliage detail at oblique or distant views
MengTo/Skills68622026年10月18日 更新

Create realistic metal surfaces in Three.js by combining structured reflections, studio lighting, scratches, fingerprints, and beveled edges. Includes polished, brushed, and aged finishes for objects such as tools, rings, and metal product parts.

中文简介正在准备中。当前显示英语简介。

  • Fixing metal that looks like gray plastic
  • Creating polished or brushed metal finishes
  • Adding patina and worn edges
MengTo/Skills68622026年10月18日 更新

查看 MengTo 的全部技能

报告此技能的问题