应用置换贴图和高光贴图

Applying displacement mapping and specular mapping

我正在尝试对地球同时应用置换贴图和高光贴图,而对月球仅应用置换贴图。

我可以将高度贴图转换为法线贴图,但是如果我使用相同的高度贴图来应用置换贴图,它不会像我预期的那样工作..

这是示例图片

你可以看到地球和月球周围的颠簸,但没有实际的高度差异。

如果我对地球应用高光贴图,地球就会变成这样

我只想让地球的海洋发光,但是我的代码把地球变成了全黑,我只能看到地球上的一些白点...

这些纹理来自这个site

这是我的顶点着色器代码和片段着色器代码

"use strict";
const loc_aPosition = 3;
const loc_aNormal = 5;
const loc_aTexture = 7;
const VSHADER_SOURCE =
`#version 300 es
layout(location=${loc_aPosition}) in vec4 aPosition;
layout(location=${loc_aNormal}) in vec4 aNormal;
layout(location=${loc_aTexture}) in vec2 aTexCoord;


uniform mat4 uMvpMatrix;
uniform mat4 uModelMatrix;    // Model matrix
uniform mat4 uNormalMatrix;   // Transformation matrix of the normal

uniform sampler2D earth_disp;
uniform sampler2D moon_disp;

//uniform float earth_dispScale;
//uniform float moon_dispScale;

//uniform float earth_dispBias;
//uniform float moon_dispBias;

uniform bool uEarth;
uniform bool uMoon;


out vec2 vTexCoord;
out vec3 vNormal;
out vec3 vPosition;


void main() 
{

  float disp;

  if(uEarth)
    disp = texture(earth_disp, aTexCoord).r; //Extracting the color information from the image
  else if(uMoon)
    disp = texture(moon_disp, aTexCoord).r; //Extracting the color information from the image

  vec4 displace = aPosition;

  float displaceFactor = 2.0;
  float displaceBias = 0.5;

  if(uEarth || uMoon) //Using Displacement Mapping
  {
    displace += (displaceFactor * disp - displaceBias) * aNormal;
    gl_Position = uMvpMatrix * displace;
  }
  else //Not using displacement mapping
    gl_Position = uMvpMatrix * aPosition;

  // Calculate the vertex position in the world coordinate
  vPosition = vec3(uModelMatrix * aPosition);

  vNormal = normalize(vec3(uNormalMatrix * aNormal));
  vTexCoord = aTexCoord;

}`;

// Fragment shader program
const FSHADER_SOURCE =
`#version 300 es
precision mediump float;

uniform vec3 uLightColor;     // Light color
uniform vec3 uLightPosition;  // Position of the light source
uniform vec3 uAmbientLight;   // Ambient light color

uniform sampler2D sun_color;
uniform sampler2D earth_color;
uniform sampler2D moon_color;

uniform sampler2D earth_bump;
uniform sampler2D moon_bump;

uniform sampler2D specularMap;


in vec3 vNormal;
in vec3 vPosition;
in vec2 vTexCoord;
out vec4 fColor;

uniform bool uIsSun;
uniform bool uIsEarth;
uniform bool uIsMoon;



vec2 dHdxy_fwd(sampler2D bumpMap, vec2 UV, float bumpScale)
{
    vec2 dSTdx  = dFdx( UV );
        vec2 dSTdy  = dFdy( UV );
        float Hll   = bumpScale * texture( bumpMap, UV ).x;
        float dBx   = bumpScale * texture( bumpMap, UV + dSTdx ).x - Hll;
        float dBy   = bumpScale * texture( bumpMap, UV + dSTdy ).x - Hll;
        return vec2( dBx, dBy );
}

vec3 pertubNormalArb(vec3 surf_pos, vec3 surf_norm, vec2 dHdxy)
{
    vec3 vSigmaX = vec3( dFdx( surf_pos.x ), dFdx( surf_pos.y ), dFdx( surf_pos.z ) );
        vec3 vSigmaY = vec3( dFdy( surf_pos.x ), dFdy( surf_pos.y ), dFdy( surf_pos.z ) );
        vec3 vN = surf_norm;        // normalized
        vec3 R1 = cross( vSigmaY, vN );
        vec3 R2 = cross( vN, vSigmaX );
        float fDet = dot( vSigmaX, R1 );
        fDet *= ( float( gl_FrontFacing ) * 2.0 - 1.0 );
        vec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 );
        return normalize( abs( fDet ) * surf_norm - vGrad );
}



void main() 
{
    vec2 dHdxy;
    vec3 bumpNormal;
    float bumpness = 1.0;
    if(uIsSun)
      fColor = texture(sun_color, vTexCoord);
    else if(uIsEarth)
    {
      fColor = texture(earth_color, vTexCoord);
      dHdxy = dHdxy_fwd(earth_bump, vTexCoord, bumpness);
    }
    else if(uIsMoon)
    {
      fColor = texture(moon_color, vTexCoord);
      dHdxy = dHdxy_fwd(moon_bump, vTexCoord, bumpness);
    }



    // Normalize the normal because it is interpolated and not 1.0 in length any more
    vec3 normal = normalize(vNormal);


    // Calculate the light direction and make its length 1.
    vec3 lightDirection = normalize(uLightPosition - vPosition);



    // The dot product of the light direction and the orientation of a surface (the normal)
    float nDotL;
    if(uIsSun)
      nDotL = 1.0;
    else
      nDotL = max(dot(lightDirection, normal), 0.0);



    // Calculate the final color from diffuse reflection and ambient reflection
    vec3 diffuse = uLightColor * fColor.rgb * nDotL;
    vec3 ambient = uAmbientLight * fColor.rgb;
    float specularFactor = texture(specularMap, vTexCoord).r; //Extracting the color information from the image




    vec3 diffuseBump;
    if(uIsEarth || uIsMoon)
    {
      bumpNormal = pertubNormalArb(vPosition, normal, dHdxy);
      diffuseBump = min(diffuse + dot(bumpNormal, lightDirection), 1.1);
    }

    vec3 specular = vec3(0.0);
    float shiness = 12.0;
    vec3 lightSpecular = vec3(1.0);

    if(uIsEarth && nDotL > 0.0)
    {
      vec3 v = normalize(-vPosition); // EyePosition
      vec3 r = reflect(-lightDirection, bumpNormal); // Reflect from the surface
      specular = lightSpecular * specularFactor * pow(dot(r, v), shiness);
    }

    //Update Final Color
    if(uIsEarth)
      fColor = vec4( (diffuse * diffuseBump * specular) + ambient, fColor.a); // Specular
    else if(uIsMoon)
      fColor = vec4( (diffuse * diffuseBump) + ambient, fColor.a);
    else if(uIsSun)
      fColor = vec4(diffuse + ambient, fColor.a);
}`;

你能告诉我我必须检查哪里吗?

如果是我,我会首先将着色器剥离为最简单的部分,然后看看我是否得到了我想要的。您想要镜面反射光,那么您是否只在着色器中进行镜面反射计算就可以得到镜面反射光

修剪着色器以仅绘制平坦的 phong 着色没有产生正确的结果

这一行

fColor = vec4( (diffuse * specular) + ambient, fColor.a);

需要

fColor = vec4( (diffuse + specular) + ambient, fColor.a);

您添加镜面反射,而不是乘以它。

"use strict";
const loc_aPosition = 3;
const loc_aNormal = 5;
const loc_aTexture = 7;
const VSHADER_SOURCE =
`#version 300 es
layout(location=${loc_aPosition}) in vec4 aPosition;
layout(location=${loc_aNormal}) in vec4 aNormal;


uniform mat4 uMvpMatrix;
uniform mat4 uModelMatrix;    // Model matrix
uniform mat4 uNormalMatrix;   // Transformation matrix of the normal

out vec3 vNormal;
out vec3 vPosition;


void main() 
{
  gl_Position = uMvpMatrix * aPosition;

  // Calculate the vertex position in the world coordinate
  vPosition = vec3(uModelMatrix * aPosition);

  vNormal = normalize(vec3(uNormalMatrix * aNormal));
}`;

// Fragment shader program
const FSHADER_SOURCE =
`#version 300 es
precision highp float;

uniform vec3 uLightColor;     // Light color
uniform vec3 uLightPosition;  // Position of the light source
uniform vec3 uAmbientLight;   // Ambient light color

in vec3 vNormal;
in vec3 vPosition;
out vec4 fColor;

void main() 
{
    vec2 dHdxy;
    vec3 bumpNormal;
    float bumpness = 1.0;
    
    fColor = vec4(0.5, 0.5, 1, 1);

    // Normalize the normal because it is interpolated and not 1.0 in length any more
    vec3 normal = normalize(vNormal);

    // Calculate the light direction and make its length 1.
    vec3 lightDirection = normalize(uLightPosition - vPosition);

    // The dot product of the light direction and the orientation of a surface (the normal)
    float nDotL;
    nDotL = max(dot(lightDirection, normal), 0.0);

    // Calculate the final color from diffuse reflection and ambient reflection
    vec3 diffuse = uLightColor * fColor.rgb * nDotL;
    vec3 ambient = uAmbientLight * fColor.rgb;
    float specularFactor = 1.0;

    bumpNormal = normal;
    vec3 specular = vec3(0.0);
    float shiness = 12.0;
    vec3 lightSpecular = vec3(1.0);

    vec3 v = normalize(-vPosition); // EyePosition
    vec3 r = reflect(-lightDirection, bumpNormal); // Reflect from the surface
    specular = lightSpecular * specularFactor * pow(dot(r, v), shiness);

    fColor = vec4( (diffuse + specular) + ambient, fColor.a); // Specular
}`;

function main() {
  const m4 = twgl.m4;
  const gl = document.querySelector('canvas').getContext('webgl2');
  if (!gl) { return alert('need webgl2'); }

  const prgInfo = twgl.createProgramInfo(gl, [VSHADER_SOURCE, FSHADER_SOURCE]);
  const verts = twgl.primitives.createSphereVertices(1, 40, 40);
  // calls gl.createBuffer, gl.bindBuffer, gl.bufferData for each array
  const bufferInfo = twgl.createBufferInfoFromArrays(gl, {
    aPosition: verts.position,
    aNormal: verts.normal,
    aTexCoord: verts.texcoord,
    indices: verts.indices,
  });
  // calls gl.bindBuffer, gl.enableVertexAttribArray, gl.vertexAttribPointer for each attribute
  twgl.setBuffersAndAttributes(gl, prgInfo, bufferInfo);
  
  twgl.resizeCanvasToDisplaySize(gl.canvas);
  gl.clearColor(0, 0, 0, 1);
  gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
  gl.enable(gl.DEPTH_TEST);
  gl.enable(gl.CULL_FACE);
  
  gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
  
  gl.useProgram(prgInfo.program);
    
  const fov = 60 * Math.PI / 180;
  const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
  const near = 0.1;
  const far = 20.0;
  const mat = m4.perspective(fov, aspect, near, far);
  m4.translate(mat, [0, 0, -3], mat);
  
  // calls gl.activeTexture, gl.bindTexture, gl.uniform
  twgl.setUniforms(prgInfo, {
    uMvpMatrix: mat,
    uModelMatrix: m4.identity(),    // Model matrix
    uNormalMatrix: m4.identity(),   // Transformation matrix of the normal
    uLightColor: [1, 1, 1],     // Light color
    uLightPosition: [2, 2, 10], // Position of the light source
    uAmbientLight: [0, 0, 0],   // Ambient light color
  });
  
  // calls gl.drawArrays or gl.drawElements
  twgl.drawBufferInfo(gl, bufferInfo);
}
main();
body { margin: 0 }
canvas { display: block; width: 100vw; height: 100vh; }
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
<canvas></canvas>

所以现在我们可以添加高光贴图

"use strict";
const loc_aPosition = 3;
const loc_aNormal = 5;
const loc_aTexCoord = 7;
const VSHADER_SOURCE =
`#version 300 es
layout(location=${loc_aPosition}) in vec4 aPosition;
layout(location=${loc_aNormal}) in vec4 aNormal;
layout(location=${loc_aTexCoord}) in vec2 aTexCoord;


uniform mat4 uMvpMatrix;
uniform mat4 uModelMatrix;    // Model matrix
uniform mat4 uNormalMatrix;   // Transformation matrix of the normal

out vec3 vNormal;
out vec3 vPosition;
out vec2 vTexCoord;


void main() 
{
  gl_Position = uMvpMatrix * aPosition;

  // Calculate the vertex position in the world coordinate
  vPosition = vec3(uModelMatrix * aPosition);

  vNormal = normalize(vec3(uNormalMatrix * aNormal));
  vTexCoord = aTexCoord;
}`;

// Fragment shader program
const FSHADER_SOURCE =
`#version 300 es
precision highp float;

uniform vec3 uLightColor;     // Light color
uniform vec3 uLightPosition;  // Position of the light source
uniform vec3 uAmbientLight;   // Ambient light color


uniform sampler2D specularMap;


in vec3 vNormal;
in vec3 vPosition;
in vec2 vTexCoord;
out vec4 fColor;

void main() 
{
    vec2 dHdxy;
    vec3 bumpNormal;
    float bumpness = 1.0;
    
    fColor = vec4(0.5, 0.5, 1, 1);

    // Normalize the normal because it is interpolated and not 1.0 in length any more
    vec3 normal = normalize(vNormal);

    // Calculate the light direction and make its length 1.
    vec3 lightDirection = normalize(uLightPosition - vPosition);

    // The dot product of the light direction and the orientation of a surface (the normal)
    float nDotL;
    nDotL = max(dot(lightDirection, normal), 0.0);

    // Calculate the final color from diffuse reflection and ambient reflection
    vec3 diffuse = uLightColor * fColor.rgb * nDotL;
    vec3 ambient = uAmbientLight * fColor.rgb;
    float specularFactor = texture(specularMap, vTexCoord).r; //Extracting the color information from the image

    bumpNormal = normal;
    vec3 specular = vec3(0.0);
    float shiness = 12.0;
    vec3 lightSpecular = vec3(1.0);

    vec3 v = normalize(-vPosition); // EyePosition
    vec3 r = reflect(-lightDirection, bumpNormal); // Reflect from the surface
    specular = lightSpecular * specularFactor * pow(dot(r, v), shiness);

    fColor = vec4( (diffuse + specular) + ambient, fColor.a); // Specular
}`;

function main() {
  const m4 = twgl.m4;
  const gl = document.querySelector('canvas').getContext('webgl2');
  if (!gl) { return alert('need webgl2'); }

  const prgInfo = twgl.createProgramInfo(gl, [VSHADER_SOURCE, FSHADER_SOURCE]);
  const verts = twgl.primitives.createSphereVertices(1, 40, 40);
  // calls gl.createBuffer, gl.bindBuffer, gl.bufferData for each array
  const bufferInfo = twgl.createBufferInfoFromArrays(gl, {
    aPosition: verts.position,
    aNormal: verts.normal,
    aTexCoord: verts.texcoord,
    indices: verts.indices,
  });
  
  const specularTex = twgl.createTexture(gl, {src: 'https://i.imgur.com/JlIJu5V.jpg'});

  function render(time) {
    twgl.resizeCanvasToDisplaySize(gl.canvas);
    gl.clearColor(0, 0, 0, 1);
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
    gl.enable(gl.DEPTH_TEST);
    gl.enable(gl.CULL_FACE);

    gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);

    // calls gl.bindBuffer, gl.enableVertexAttribArray, gl.vertexAttribPointer for each attribute
    twgl.setBuffersAndAttributes(gl, prgInfo, bufferInfo);

    gl.useProgram(prgInfo.program);

    const fov = 60 * Math.PI / 180;
    const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
    const near = 0.1;
    const far = 20.0;
    const mat = m4.perspective(fov, aspect, near, far);
    
    m4.translate(mat, [0, 0, -3], mat);
    
    const model = m4.rotationY(time / 1000);

    m4.multiply(mat, model, mat);

    // calls gl.activeTexture, gl.bindTexture, gl.uniform
    twgl.setUniforms(prgInfo, {
      uMvpMatrix: mat,
      uModelMatrix: model,    // Model matrix
      uNormalMatrix: model,   // Transformation matrix of the normal
      uLightColor: [1, 1, 1],     // Light color
      uLightPosition: [2, 2, 10], // Position of the light source
      uAmbientLight: [0, 0, 0],   // Ambient light color
      specularMap: specularTex,
    });

    // calls gl.drawArrays or gl.drawElements
    twgl.drawBufferInfo(gl, bufferInfo);
    
    requestAnimationFrame(render);
  }
  requestAnimationFrame(render);
}
main();
body { margin: 0 }
canvas { display: block; width: 100vw; height: 100vh; }
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
<canvas></canvas>

那么你可以说你不应该在你的着色器上使用大量的布尔条件。要么制作不同的着色器以找到一种无需布尔值的方法。例如我们不需要

uniform sampler2D earth_disp;
uniform sampler2D moon_disp;

uniform sampler2D sun_color;
uniform sampler2D earth_color;
uniform sampler2D moon_color;

uniform sampler2D earth_bump;
uniform sampler2D moon_bump;

uniform bool uIsSun;
uniform bool uIsEarth;
uniform bool uIsMoon;

我们可以

uniform sampler2D displacementMap;
uniform sampler2D surfaceColor;
uniform sampler2D bumpMap;

然后我们可以将displacementMapbumpMap设置为单个像素0,0,0,0纹理,并且不会有位移和凹凸。

至于太阳的不同光照,鉴于太阳既不使用凹凸贴图也不使用置换贴图,甚至根本不使用光照,使用不同的着色器可能会更好,但是,我们也可以只添加一个 maxDot 像这样的值

uniform float maxDot;

...

   nDotL = max(dot(lightDirection, normal), maxDot)

如果 maxDot 为零,我们将得到一个正常的点积。如果 maxDot 是一个,我们就没有照明。

"use strict";
const loc_aPosition = 3;
const loc_aNormal = 5;
const loc_aTexture = 7;
const VSHADER_SOURCE =
`#version 300 es
layout(location=${loc_aPosition}) in vec4 aPosition;
layout(location=${loc_aNormal}) in vec3 aNormal;
layout(location=${loc_aTexture}) in vec2 aTexCoord;


uniform mat4 uMvpMatrix;
uniform mat4 uModelMatrix;    // Model matrix
uniform mat4 uNormalMatrix;   // Transformation matrix of the normal

uniform sampler2D displacementMap;

out vec2 vTexCoord;
out vec3 vNormal;
out vec3 vPosition;

void main() 
{

  float disp;

  disp = texture(displacementMap, aTexCoord).r; 
  vec4 displace = aPosition;

  float displaceFactor = 0.1;
  float displaceBias = 0.0;

  displace.xyz += (displaceFactor * disp - displaceBias) * aNormal;
  gl_Position = uMvpMatrix * displace;

  // Calculate the vertex position in the world coordinate
  vPosition = vec3(uModelMatrix * aPosition);

  vNormal = normalize(mat3(uNormalMatrix) * aNormal);
  vTexCoord = aTexCoord;

}`;

// Fragment shader program
const FSHADER_SOURCE =
`#version 300 es
precision highp float;

uniform vec3 uLightColor;     // Light color
uniform vec3 uLightPosition;  // Position of the light source
uniform vec3 uAmbientLight;   // Ambient light color

uniform sampler2D surfaceColor;
uniform sampler2D bumpMap;
uniform sampler2D specularMap;

uniform float maxDot;

in vec3 vNormal;
in vec3 vPosition;
in vec2 vTexCoord;
out vec4 fColor;


vec2 dHdxy_fwd(sampler2D bumpMap, vec2 UV, float bumpScale)
{
    vec2 dSTdx  = dFdx( UV );
        vec2 dSTdy  = dFdy( UV );
        float Hll   = bumpScale * texture( bumpMap, UV ).x;
        float dBx   = bumpScale * texture( bumpMap, UV + dSTdx ).x - Hll;
        float dBy   = bumpScale * texture( bumpMap, UV + dSTdy ).x - Hll;
        return vec2( dBx, dBy );
}

vec3 pertubNormalArb(vec3 surf_pos, vec3 surf_norm, vec2 dHdxy)
{
    vec3 vSigmaX = vec3( dFdx( surf_pos.x ), dFdx( surf_pos.y ), dFdx( surf_pos.z ) );
        vec3 vSigmaY = vec3( dFdy( surf_pos.x ), dFdy( surf_pos.y ), dFdy( surf_pos.z ) );
        vec3 vN = surf_norm;        // normalized
        vec3 R1 = cross( vSigmaY, vN );
        vec3 R2 = cross( vN, vSigmaX );
        float fDet = dot( vSigmaX, R1 );
        fDet *= ( float( gl_FrontFacing ) * 2.0 - 1.0 );
        vec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 );
        return normalize( abs( fDet ) * surf_norm - vGrad );
}



void main() 
{
    vec2 dHdxy;
    vec3 bumpNormal;
    float bumpness = 1.0;
    fColor = texture(surfaceColor, vTexCoord);
    dHdxy = dHdxy_fwd(bumpMap, vTexCoord, bumpness);

    // Normalize the normal because it is interpolated and not 1.0 in length any more
    vec3 normal = normalize(vNormal);

    // Calculate the light direction and make its length 1.
    vec3 lightDirection = normalize(uLightPosition - vPosition);

    // The dot product of the light direction and the orientation of a surface (the normal)
    float nDotL;
    nDotL = max(dot(lightDirection, normal), maxDot);



    // Calculate the final color from diffuse reflection and ambient reflection
    vec3 diffuse = uLightColor * fColor.rgb * nDotL;
    vec3 ambient = uAmbientLight * fColor.rgb;
    float specularFactor = texture(specularMap, vTexCoord).r; //Extracting the color information from the image




    vec3 diffuseBump;
    bumpNormal = pertubNormalArb(vPosition, normal, dHdxy);
    diffuseBump = min(diffuse + dot(bumpNormal, lightDirection), 1.1);

    vec3 specular = vec3(0.0);
    float shiness = 12.0;
    vec3 lightSpecular = vec3(1.0);

    vec3 v = normalize(-vPosition); // EyePosition
    vec3 r = reflect(-lightDirection, bumpNormal); // Reflect from the surface
    specular = lightSpecular * specularFactor * pow(dot(r, v), shiness);

    //Update Final Color
    fColor = vec4( (diffuse * diffuseBump + specular) + ambient, fColor.a); // Specular
}`;

function main() {
  const m4 = twgl.m4;
  const gl = document.querySelector('canvas').getContext('webgl2');
  if (!gl) { return alert('need webgl2'); }

  const prgInfo = twgl.createProgramInfo(gl, [VSHADER_SOURCE, FSHADER_SOURCE]);
  const verts = twgl.primitives.createSphereVertices(1, 40, 40);
  // calls gl.createBuffer, gl.bindBuffer, gl.bufferData for each array
  const bufferInfo = twgl.createBufferInfoFromArrays(gl, {
    aPosition: verts.position,
    aNormal: verts.normal,
    aTexCoord: verts.texcoord,
    indices: verts.indices,
  });
  
  const textures = twgl.createTextures(gl, {
    zero: { src: new Uint8Array([0, 0, 0, 0])},
    earthSpecular: { src: 'https://i.imgur.com/JlIJu5V.jpg' },
    earthColor: { src: 'https://i.imgur.com/eCpD7bM.jpg' },
    earthBump: { src: 'https://i.imgur.com/LzFNOP8.jpg' },
    sunColor: { src: 'https://i.imgur.com/gl8zBLI.jpg', },
    moonColor: { src: 'https://i.imgur.com/oLiU4fm.jpg', },
    moonBump: { src: 'https://i.imgur.com/bDnjW8C.jpg', },
  });
  
  function render(time) {
    // calls gl.bindBuffer, gl.enableVertexAttribArray, gl.vertexAttribPointer for each attribute
    twgl.setBuffersAndAttributes(gl, prgInfo, bufferInfo);

    twgl.resizeCanvasToDisplaySize(gl.canvas);
    gl.enable(gl.DEPTH_TEST);
    gl.enable(gl.CULL_FACE);

    gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);

    gl.useProgram(prgInfo.program);

    const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
    const fov = 60 * Math.PI / 180 / aspect;
    const near = 0.1;
    const far = 20.0;
    const viewProjection = m4.perspective(fov, aspect, near, far);
    m4.translate(viewProjection, [0, 0, -6], viewProjection);

    draw([0, 0, 0], {
       displacementMap: textures.earthBump,
       bumpMap: textures.earthBump,
       surfaceColor: textures.earthColor,
       specularMap: textures.earthSpecular,
       maxDot: 0,
       uAmbientLight: [0, 0, 0],
     });
    draw([-2.2, 0, 0], {
       displacementMap: textures.zero,
       bumpMap: textures.zero,
       surfaceColor: textures.sunColor,
       specularMap: textures.zero,
       maxDot: 1,
       uAmbientLight: [0, 0, 0],
     });
    draw([2.2, 0, 0], {
       displacementMap: textures.moonBump,
       bumpMap: textures.moonBump,
       surfaceColor: textures.moonColor,
       specularMap: textures.zero,
       maxDot: 0,
       uAmbientLight: [0, 0, 0],
     });

    function draw(translation, uniforms) {
       const model = m4.translation(translation);
       m4.rotateY(model, time / 1000, model);

      // calls gl.activeTexture, gl.bindTexture, gl.uniform
      twgl.setUniforms(prgInfo, {
        uMvpMatrix: m4.multiply(viewProjection, model),
        uModelMatrix: model,    // Model matrix
        uNormalMatrix: m4.transpose(m4.inverse(model)),   // Transformation matrix of the normal
        uLightColor: [1, 1, 1],     // Light color
        uLightPosition: [2, 2, 10], // Position of the light source
        uAmbientLight: [1, 0, 0],   // Ambient light color
      });
      twgl.setUniforms(prgInfo, uniforms);

      // calls gl.drawArrays or gl.drawElements
      twgl.drawBufferInfo(gl, bufferInfo);
    }
    requestAnimationFrame(render);
  }
  requestAnimationFrame(render);
}
main();
body { margin: 0 }
canvas { display: block; width: 100vw; height: 100vh; }
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
<canvas></canvas>

至于位移,位移只对顶点起作用,所以你需要在你的球体中有很多顶点才能看到任何位移

还有一个与位移相关的错误。您将法线作为 vec4 传递,这一行

displace += (displaceFactor * disp - displaceBias) * aNormal;

最后添加了一个 vec4 位移。换句话说,假设您从 vec4(1,0,0,1)a_Position 开始,它位于球体的左侧。 aNormal 因为您将其声明为 vec4 可能也是 vec4(1,0,0,1)。假设您实际上是通过缓冲区中的属性向它传递 vec3 普通数据,W 的默认值为 1。假设 disp 为 1,displaceFactor 为 2,displaceBias 为 0.5,即你有什么。你最终得到

displace = vec4(1,0,0,1) + (2 * 1 + 0.5) * vec4(1,0,0,1)
displace = vec4(1,0,0,1) + (1.5) * vec4(1,0,0,1)
displace = vec4(1,0,0,1) + vec4(1.5,0,0,1.5)
displace = vec4(2.5,0,0,2.5)

但您不希望 W 为 2.5。一种解决方法是仅使用法线的 xyz 部分。

displace.xyz += (displaceFactor * disp - displaceBias) * aNormal.xyz;

更正常的修复方法是只将普通属性声明为 vec3

in vec3 aNormal;

displace.xyz += (displaceFactor * disp - displaceBias) * aNormal;

在我上面的例子中,球体只有半径 = 1,所以我们只想稍微调整一下这个位移。我将 displaceFactor 设置为 0.1,将 displaceBias 设置为 0.