Skip to content

Instantly share code, notes, and snippets.

@sketchpunk
Created September 16, 2025 20:13
Show Gist options
  • Select an option

  • Save sketchpunk/633272faf3ec37767db5686059c86fc1 to your computer and use it in GitHub Desktop.

Select an option

Save sketchpunk/633272faf3ec37767db5686059c86fc1 to your computer and use it in GitHub Desktop.

Revisions

  1. sketchpunk created this gist Sep 16, 2025.
    130 changes: 130 additions & 0 deletions SkyGradientMaterial.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,130 @@
    import * as THREE from 'three';

    export default class SkyGradientMaterial extends THREE.RawShaderMaterial {
    static createBox(scl = 1): THREE.Mesh {
    const geo = new THREE.BoxGeometry(2, 2, 2);
    const mesh = new THREE.Mesh(geo, new SkyGradientMaterial());
    mesh.scale.setScalar(scl);
    return mesh;
    }

    static createDome(scl = 1): THREE.Mesh {
    const geo = new THREE.SphereGeometry(1, 16, 16);
    const mesh = new THREE.Mesh(geo, new SkyGradientMaterial());
    mesh.scale.setScalar(scl);
    return mesh;
    }

    constructor(props = {}) {
    super();

    // Merge custom props with default options
    const opts = Object.assign(
    {
    ramp: [0, '#DCDCDC', 0.3, '#DCDCDC', 0.7, '#60B8D3', 1, '#60B8D3'],
    depth: true,
    },
    props,
    );

    this.name = 'SkyGradientMaterial';
    this.glslVersion = THREE.GLSL3;
    this.depthTest = opts.depth;
    this.transparent = false;
    this.side = THREE.BackSide;

    this.uniforms = {
    rampWeight: { value: [0, 0, 0, 0, 0, 0, 0, 0] },
    rampSize: { value: 0 },
    rampColor: {
    value: [
    new THREE.Color(),
    new THREE.Color(),
    new THREE.Color(),
    new THREE.Color(),
    new THREE.Color(),
    new THREE.Color(),
    new THREE.Color(),
    new THREE.Color(),
    ],
    },
    };

    this.setRamp(opts.ramp);

    this.vertexShader = `
    in vec3 position;
    in vec3 normal;
    in vec2 uv;
    uniform mat4 modelMatrix;
    uniform mat4 viewMatrix;
    uniform mat4 projectionMatrix;
    out vec3 fragLPos;
    void main(){
    vec4 wpos = modelMatrix * vec4( position, 1.0 );
    gl_Position = projectionMatrix * viewMatrix * wpos;
    fragLPos = position;
    }`;

    this.fragmentShader = `precision mediump float;
    in vec3 fragLPos;
    out vec4 outColor;
    uniform vec3[8] rampColor;
    uniform float[8] rampWeight;
    uniform int rampSize;
    vec3 colorLerpRamp( vec3[8] color, float[8] wgt, float t, int cnt ){
    for( int i=1; i < cnt; i++ ){
    if( t <= wgt[ i ] ){
    return mix(
    color[ i-1 ],
    color[ i ],
    // smoothstep( wgt[ i-1 ], wgt[ i ] , t )
    ( t - wgt[ i-1 ] ) / ( wgt[ i ] - wgt[ i-1 ] )
    );
    }
    }
    return color[ cnt-1 ];
    }
    void main(){
    outColor.a = 1.0;
    // Solid Color
    if( rampSize == 1 ){
    outColor.rgb = rampColor[0];
    return;
    }
    float t = clamp( fragLPos.y * 0.5 + 0.5 , 0.0, 1.0 );
    // t = smoothstep( 0.0, 1.0, t );
    outColor.rgb = colorLerpRamp( rampColor, rampWeight, t, rampSize );
    // Gamma correction 1.0/2.2 = 0.4545...
    outColor.rgb = pow( outColor.rgb, vec3(0.4545) );
    }`;
    }

    setRamp(ramp) {
    const cAry = this.uniforms.rampColor.value;
    const wAry = this.uniforms.rampWeight.value;
    const cnt = Math.min(8, Math.floor(ramp.length / 2));

    this.uniforms.rampSize.value = cnt;

    let j;
    for (let i = 0; i < cnt; i++) {
    j = i * 2;
    wAry[i] = ramp[j];
    cAry[i].set(ramp[j + 1]);
    }

    return this;
    }
    }