跳轉到內容

GLSL 程式設計/GLUT/光澤紋理

來自 Wikibooks,開放世界中的開放書籍
從國際空間站 (ISS) 看太平洋上空的日落,帶有鏡面反射。

本教程涵蓋了部分光澤、紋理表面的逐畫素光照

它結合了紋理球體教程平滑鏡面反射教程的著色器程式碼,以計算逐畫素光照,並使用紋理的 RGB 分量確定漫反射的材質顏色,並使用同一紋理的 A 分量確定鏡面反射的強度。如果您還沒有閱讀紋理球體教程平滑鏡面反射教程,現在是一個非常好的機會去閱讀它們。

光澤對映

[編輯 | 編輯原始碼]

光照紋理表面教程介紹了使用紋理影像的 RGB 分量確定漫反射材質常數的概念。在這裡,我們擴充套件了這種技術,並使用同一紋理影像的 A(alpha)分量來確定鏡面反射的強度。僅使用一個紋理提供了顯著的效能優勢,特別是在某些情況下,RGBA 紋理查詢與 RGB 紋理查詢一樣昂貴。

如果紋理影像的“光澤”(即鏡面反射的強度)編碼在 RGBA 紋理影像的 A(alpha)分量中,我們可以簡單地將鏡面反射的材質常數乘以紋理影像的 alpha 分量。是在鏡面反射教程中引入的,並出現在 Phong 反射模型的鏡面反射項中

如果乘以紋理影像的 alpha 分量,該項將達到其最大值(即表面是光澤的),其中 alpha 為 1,並且它為 0(即表面根本不光澤),其中 alpha 為 0。

帶有透明水的地球地圖,即水面的 alpha 分量為 0,陸地的 alpha 分量為 1。

逐畫素光照的著色器程式碼

[編輯 | 編輯原始碼]

著色器程式碼是平滑鏡面反射教程中的逐畫素光照和紋理球體教程中的紋理的組合。類似於光照紋理表面教程textureColor中的紋理顏色的 RGB 分量乘以環境光和漫反射光。

在左側的特定紋理影像中,水的 alpha 分量為 0,陸地的 alpha 分量為 1。但是,應該是水是光澤的,而陸地不是。因此,對於這個特定的影像,我們應該將鏡面材質顏色乘以(1.0 - textureColor.a)。另一方面,通常的光澤貼圖需要乘以textureColor.a。(注意,對著色器程式進行這種更改是多麼容易。)

頂點著色器是

attribute vec3 v_coord;
attribute vec3 v_normal;
varying vec4 position;  // position of the vertex (and fragment) in world space
varying vec3 varyingNormalDirection;  // surface normal vector in world space
varying vec4 texCoords; // the texture coordinates
uniform mat4 m, v, p;
uniform mat3 m_3x3_inv_transp;

void main()
{
  vec4 v_coord4 = vec4(v_coord, 1.0);
  mat4 mvp = p*v*m;
  position = m * v_coord4;
  varyingNormalDirection = normalize(m_3x3_inv_transp * v_normal);

  texCoords = v_coord4;
  gl_Position = mvp * v_coord4;
}

片段著色器變為

varying vec4 position;  // position of the vertex (and fragment) in world space
varying vec3 varyingNormalDirection;  // surface normal vector in world space
varying vec4 texCoords; // the texture coordinates
uniform mat4 m, v, p;
uniform mat4 v_inv;
uniform sampler2D mytexture;
 
struct lightSource
{
  vec4 position;
  vec4 diffuse;
  vec4 specular;
  float constantAttenuation, linearAttenuation, quadraticAttenuation;
  float spotCutoff, spotExponent;
  vec3 spotDirection;
};
lightSource light0 = lightSource(
  vec4(0.0,  1.0,  0.0, 1.0),
  vec4(1.0,  1.0,  1.0, 1.0),
  vec4(1.0,  1.0,  1.0, 1.0),
  0.0, 1.0, 0.0,
  180.0, 0.0,
  vec3(0.0, 0.0, 0.0)
);
vec4 scene_ambient = vec4(0.2, 0.2, 0.2, 1.0);

struct material
{
  vec4 ambient;
  vec4 diffuse;
  vec4 specular;
  float shininess;
};
material frontMaterial = material(
  vec4(0.2, 0.2, 0.2, 1.0),
  vec4(1.0, 0.8, 0.8, 1.0),
  vec4(1.0, 1.0, 1.0, 1.0),
  5.0
);
 
void main()
{
  vec3 normalDirection = normalize(varyingNormalDirection);
  vec3 viewDirection = normalize(vec3(v_inv * vec4(0.0, 0.0, 0.0, 1.0) - position));
  vec3 lightDirection;
  float attenuation;

  vec2 longitudeLatitude = vec2((atan(texCoords.y, texCoords.x) / 3.1415926 + 1.0) * 0.5,
                                (asin(texCoords.z) / 3.1415926 + 0.5));
  // unusual processing of texture coordinates
  
  vec4 textureColor = texture2D(mytexture, longitudeLatitude);
 
  if (0.0 == light0.position.w) // directional light?
    {
      attenuation = 1.0; // no attenuation
      lightDirection = normalize(vec3(light0.position));
    } 
  else // point light or spotlight (or other kind of light) 
    {
      vec3 positionToLightSource = vec3(light0.position - position);
      float distance = length(positionToLightSource);
      lightDirection = normalize(positionToLightSource);
      attenuation = 1.0 / (light0.constantAttenuation
                           + light0.linearAttenuation * distance
                           + light0.quadraticAttenuation * distance * distance);
 
      if (light0.spotCutoff <= 90.0) // spotlight?
        {
          float clampedCosine = max(0.0, dot(-lightDirection, light0.spotDirection));
          if (clampedCosine < cos(radians(light0.spotCutoff))) // outside of spotlight cone?
            {
              attenuation = 0.0;
            }
          else
            {
              attenuation = attenuation * pow(clampedCosine, light0.spotExponent);   
            }
        }
    }
 
  vec3 ambientLighting = vec3(scene_ambient) * vec3(textureColor);
 
  vec3 diffuseReflection = attenuation 
    * vec3(light0.diffuse) * vec3(textureColor)
    * max(0.0, dot(normalDirection, lightDirection));
 
  vec3 specularReflection;
  if (dot(normalDirection, lightDirection) < 0.0) // light source on the wrong side?
    {
      specularReflection = vec3(0.0, 0.0, 0.0); // no specular reflection
    }
  else // light source on the right side
    {
        specularReflection = attenuation * vec3(light0.specular) * vec3(frontMaterial.specular) * (1.0 - textureColor.a)
          // for usual gloss maps: "* textureColor.a"
        * pow(max(0.0, dot(reflect(-lightDirection, normalDirection), viewDirection)), frontMaterial.shininess);
    }
 
  gl_FragColor = vec4(ambientLighting + diffuseReflection + specularReflection, 1.0);
}

紋理和球體必須按照紋理球體教程中所述進行設定。

對於上述特定紋理影像,此著色器的有用修改是將漫反射材質顏色設定為 alpha 分量為 0 的深藍色。

逐頂點光照的著色器程式碼

[編輯 | 編輯原始碼]

平滑鏡面反射教程中所述,鏡面反射通常無法使用逐頂點光照渲染得很好。但是,有時由於效能限制,別無選擇。為了在光照紋理表面教程的著色器程式碼中包含光澤對映,應該使用以下程式碼替換片段著色器

varying vec3 diffuseColor;
    // the interpolated diffuse Phong lighting
varying vec3 specularColor;
    // the interpolated specular Phong lighting
varying vec4 texCoords;
    // the interpolated texture coordinates
uniform sampler2D mytexture;

void main(void)
{
    vec2 longitudeLatitude = vec2((atan(texCoords.y, texCoords.x) / 3.1415926 + 1.0) * 0.5,
                                  (asin(texCoords.z) / 3.1415926 + 0.5));
    // unusual processing of texture coordinates

    vec4 textureColor = 
        texture2D(mytexture, longitudeLatitude);
    gl_FragColor = vec4(diffuseColor * vec3(textureColor)
        + specularColor * (1.0 - textureColor.a), 1.0);
}

請注意,通常的光澤貼圖需要乘以textureColor.a,而不是(1.0 - textureColor.a)

恭喜!您完成了一個關於光澤對映的重要教程。我們已經瞭解了

  • 什麼是光澤對映。
  • 如何為逐畫素光照實現它。
  • 如何為逐頂點光照實現它。

進一步閱讀

[編輯 | 編輯原始碼]

如果您還想了解更多


< GLSL 程式設計/GLUT

除非另有說明,否則本頁上的所有示例原始碼都授予公共領域。
返回OpenGL 程式設計 - 光照部分 返回GLSL 程式設計 - GLUT 部分
華夏公益教科書