Getting realistic light colours from three sky shader

I’m trying to use the sky shader from the three examples to get realistic and dynamic lighting for my scenes.

I’m pointing some orthographic cameras at the sun position, directly up, and directly down, and using the .readRenderTargetPixels renderer method to dynamically choose colours for my scene’s directional and hemispheric lights (code below).

After a bit of wrangling, it’s gotten really nice results for crepuscular lighting, but when the sun gets above about 15-20 degrees in elevation everything gets too blue. I realise that this might just be realistic and in real life our eyes would adjust or something, but wondering if there’s some way I’m processing the colours that could be better, or if I’m thinking about this wrong.

Here are examples of the results I’m getting, and inset are the RGBA readouts from readRenderTargetPixels, for the Sun, Up, and Down cameras, from top to bottom. Side note - someone here seems to be getting neat RGB values from 0-255 directly from this method and I’d love to know how they’re doing that.

Here is my code for getting the rendered pixels:

  private getVectorRGBA({ x, y, z, }: 
    { x: number; y: number; z: number; }): 
    Float32Array {
    
    // Camera
    const orthoCamera = new THREE.OrthographicCamera();
    orthoCamera.lookAt(x, y, z);
    orthoCamera.translateOnAxis(new THREE.Vector3(0, 0, -1), 20);
    this.scene.add(orthoCamera, new THREE.CameraHelper(orthoCamera));

    // Render target
    let skyTexture = new THREE.WebGLRenderTarget(
      this.sizes.width,
      this.sizes.height,
      {
        minFilter: THREE.LinearFilter,
        magFilter: THREE.NearestFilter,
        format: THREE.RGBAFormat,
        type: THREE.FloatType,
      }
    );

    // Renderer
    this.renderer.setRenderTarget(skyTexture);
    this.renderer.clear();
    this.renderer.render(this.scene, orthoCamera);
    this.renderer.setRenderTarget(null);
    const read = new Float32Array(4);
    this.renderer.readRenderTargetPixels(skyTexture, 10, 10, 1, 1, read);
    console.log(read);
    return read;
  }

And here is how I’m putting that into my directional light (similar method for the hemispheric light):

  private makeSunlight(): THREE.DirectionalLight {

    // Position
    const sunlightPosition = { x: 0, y: 0, z: 0 };
    for (let dimension of ["x", "y", "z"]) {
      sunlightPosition[dimension] =
        (this.sky as Sky).material.uniforms["sunPosition"].value[dimension] *
        (this.sky as Sky).scale[dimension];
    }

    // Color
    const [sunR, sunG, sunB] = this.getVectorRGBA(sunlightPosition);
    const sunlightColor = new THREE.Color().setRGB(
      sunR,
      sunG,
      sunB,
      THREE.SRGBColorSpace
    );

    // Directional light
    const sunlight = new THREE.DirectionalLight(sunlightColor, 0.5);
    sunlight.position.set(
      sunlightPosition.x,
      sunlightPosition.y,
      sunlightPosition.z
    );
    return sunlight;
  }

Here are my renderer settings - I don’t know much about these as I copied them from here:

  renderer.toneMapping = THREE.ACESFilmicToneMapping;
  renderer.toneMappingExposure = 0.5;

Finally, here are my sky settings:

    (this.sky as Sky).material.uniforms["turbidity"].value = 10;
    (this.sky as Sky).material.uniforms["rayleigh"].value = 3;
    (this.sky as Sky).material.uniforms["mieCoefficient"].value = 0.005;
    (this.sky as Sky).material.uniforms["mieDirectionalG"].value = 0.7;

Apologies for any shoddy code: I’m new to TypeScript! If anyone has any ideas for dynamically adjusting the overly-blue light for higher sun elevations, especially in a physically-based / realistic way, that woud be great. Also, bonus Qs:

  1. General thoughts on whether this is a good way to go about this, especially with regards to realism and perfomance (I eventually want to use this for smooth lighting transitions between times of day)?
  2. Thoughts on the best way to get the hemispheric light’s groundColor? I’m thinking of pointing the down camera at a relevant texture, but then there’s a chicken-and-egg situation because the texture needs to be lighted in the first place (but I’m sure I could get round this by just doing the other lights first)
  3. How to get 0-255 range RGB values from the .readRenderTargetPixels method?