4

I am using three.js to develop 3D games on the WeChat, but I have a problem, how three.js set the background color of the gradient. I see the background color on the document, but there is no background color of the gradual change. Please make clear that I am Chinese bad English.

7asdfad
  • 43
  • 1
  • 4

3 Answers3

5

For advanced effects, try using https://github.com/mattdesl/three-vignette-background or see how it works. For something simple, make a CSS gradient and put it behind the canvas. You can use CSS to make the gradient and then make the threejs canvas transparent.

Don McCurdy
  • 10,975
  • 2
  • 37
  • 75
  • JS code runs on WeChat environment, WeChat is not a browser without windown, and DOM objects, and only one running JS core does not support HTML, CSS and so on, it can only use canvas2d or webgl – 7asdfad May 08 '18 at 06:03
  • 2
    Or generate a canvas and draw a gradient on it, then apply it to the `.background` of the scene with `THREE.CanvasTexture()`. https://jsfiddle.net/prisoner849/311z5ceo/ – prisoner849 May 08 '18 at 07:00
2

Here is reusable function with ESM

import {
  Mesh,
  BackSide,
  SphereGeometry,
  ShaderMaterial,
  Color
} from 'three'

const SKY_COLOR = 0x999999
const GROUND_COLOR = 0x242424
const SKY_SIZE = 1000000

function addSkyGradient(scene) {
      const vertexShader = `
      varying vec3 vWorldPosition;
            void main() {
                vec4 worldPosition = modelMatrix * vec4( position, 1.0 );
                vWorldPosition = worldPosition.xyz;
                gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
            }`
      const fragmentShader = `
      uniform vec3 topColor;
            uniform vec3 bottomColor;
            varying vec3 vWorldPosition;
            void main() {
                float h = normalize( vWorldPosition).z;
                gl_FragColor = vec4( mix( bottomColor, topColor, max( h, 0.0 ) ), 1.0 );
            }`
      const uniforms = {
        topColor: { value: new Color(SKY_COLOR) },
        bottomColor: { value: new Color(GROUND_COLOR) }
      }
      const skyGeo = new SphereGeometry(SKY_SIZE, 32, 15)
      const skyMat = new ShaderMaterial({
        uniforms,
        vertexShader,
        fragmentShader,
        side: BackSide
      })
      const sky = new Mesh(skyGeo, skyMat)
      scene.add(sky)
}

My Document

  • This one is good for a "sky dome" with a gradient color. The same approach is used in [this hemisphere example](https://threejs.org/examples/#webgl_lights_hemisphere). – XåpplI'-I0llwlg'I - Aug 08 '23 at 13:22
1

It's simple to do this with a shader

var myGradient = new THREE.Mesh(
  new THREE.PlaneBufferGeometry(2,2,1,1),
  new THREE.ShaderMaterial({
    uniforms: {
      uColorA: { value: new THREE.Color },
      uColorB: { value: new THREE.Color }
    },
    vertexShader: require('./gradient.vert'),
    fragmentShader: require('./gradient.frag')
  })
)

Now i used to render this as such:

myGradient.material.depthWrite = false
myGradient.renderOrder = -99999

Which would cause this mesh to render first, and then everything over it. But this actually may not be the best solution, it might be better to render it last, over everything else?

gradient.vert:

varying vec2 vUv;
void main(){
  vUv = uv;
  float depth = -1.; //or maybe 1. you can experiment
  gl_Position = vec4(position.xy, depth, 1.);
}

gradient.frag:

varying vec2 vUv;
uniform vec3 uColorA;
uniform vec3 uColorB;
void main(){
  gl_FragColor = vec4(
    mix( uColorA, uColorB, vec3(vUv.y)),
    1.
  );
}
pailhead
  • 5,162
  • 2
  • 25
  • 46