天天看點

渲染世界的OPENGL<12> 一些有趣的紋理着色器

從着色器通路紋理貼圖是非常簡單的。紋理坐标将會作為屬性傳遞到我們的頂點着色器,在片段着色器當中,這些屬性通常是在頂點之間進行平滑插值的。片段着色器使用這些內插補點紋理坐标來對紋理進行采樣。

(1)隻處理紋理單元

// The TexturedIdentity Shader
// Vertex Shader
// Richard S. Wright Jr.
// OpenGL SuperBible
//最為簡單的紋理渲染器
#version 130
//傳遞到頂點着色器當中一個頂點坐标
in vec4 vVertex;
//傳遞到頂點着色器當中一個紋理坐标,注意核心部分就是這個s和t紋理坐标的輸入
//頂點屬性vTexCoords以及輸出變量vVaringTexCoords。用紋理坐标在三角形表面進行內插補點
//所需要的,就是這些
in vec2 vTexCoords;

smooth out vec2 vVaryingTexCoords;

void main(void) 
    { 
    vVaryingTexCoords = vTexCoords;
    gl_Position = vVertex;
    }
           

以上就是僅僅處理紋理單元的頂點着色器,在頂點着色器當中,僅僅對頂點坐标以及頂點的紋理坐标s、t進行相應的傳遞以及處理。因為其為2D紋理圖像,是以紋理坐标僅僅包含s、t。

// The TexturedIdentity Shader
// Fragment Shader
// Richard S. Wright Jr.
// OpenGL SuperBible
#version 130

//注意這個新的資料變量類型sample2D
//一個采樣器(sample)實際上就是一個這麼好設計(使用glUniform1i來設定值)它代表我們将要
//采樣的紋理所綁定的紋理單元。sample2D中的2D表示這是一個2D紋理,我們也可以使用1D
//2D或者3D等類型的采樣器。 這個值總是設定為0,代表訓示紋理單元0。
uniform sampler2D colorMap;

out vec4 vFragColor;
smooth in vec2 vVaryingTexCoords;


void main(void)
   { 
   //将目前傳遞進片段着色器當中的st紋理坐标和colormap采樣進行texture函數
   //進而得到最終要進行光栅化的片段顔色。
   vFragColor = texture(colorMap, vVaryingTexCoords.st);
   }
           

如上就是紋理坐标的片段着色器,其中僅僅使用了一個texture函數作為fragColor的值。

#pragma comment(lib,"GLTools.lib")

#include <GLTools.h>            // OpenGL toolkit
#include <GLShaderManager.h>    // Shader Manager Class
#include <GL/glut.h>            // Windows FreeGlut equivalent


GLBatch triangleBatch;
GLShaderManager shaderManager;

GLint   myTexturedIdentityShader;
GLuint  textureID;

///////////////////////////////////////////////////////////////////////////////
// Window has changed size, or has just been created. In either case, we need
// to use the window dimensions to set the viewport and the projection matrix.
void ChangeSize(int w, int h)
{
    glViewport(, , w, h);
}


// 加載一個tga圖像作為一個2D紋理,并且進行必要的初始化
bool LoadTGATexture(const char *szFileName, GLenum minFilter, GLenum magFilter, GLenum wrapMode)
{
    GLbyte *pBits;
    int nWidth, nHeight, nComponents;
    GLenum eFormat;

    // 讀取對應檔案當下的圖像
    pBits = gltReadTGABits(szFileName, &nWidth, &nHeight, &nComponents, &eFormat);
    if (pBits == NULL)
        return false;
    //設定對應圖象的功能以及設定環繞模式
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapMode);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapMode);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magFilter);

    glPixelStorei(GL_UNPACK_ALIGNMENT, );
    glTexImage2D(GL_TEXTURE_2D, , nComponents, nWidth, nHeight, ,
        eFormat, GL_UNSIGNED_BYTE, pBits);
    //在設定完成參數之後,可以釋放對應的字元指針
    free(pBits);

    if (minFilter == GL_LINEAR_MIPMAP_LINEAR ||
        minFilter == GL_LINEAR_MIPMAP_NEAREST ||
        minFilter == GL_NEAREST_MIPMAP_LINEAR ||
        minFilter == GL_NEAREST_MIPMAP_NEAREST)
        //生成對應的2維圖像
        glGenerateMipmap(GL_TEXTURE_2D);

    return true;
}


///////////////////////////////////////////////////////////////////////////////
// 設定渲染環境
void SetupRC()
{
    // 背景
    glClearColor(f, f, f, f);
    //渲染器初始化
    shaderManager.InitializeStockShaders();

    // Load up a triangle
    GLfloat vVerts[] = { -f, f, f,
        f, f, f,
        f, f, f };

    GLfloat vTexCoords[] = { f, f,
        f, f,
        f, f };

    triangleBatch.Begin(GL_TRIANGLES, , );
    triangleBatch.CopyVertexData3f(vVerts);
    triangleBatch.CopyTexCoordData2f(vTexCoords, );
    triangleBatch.End();
    //建立對應的着色器
    myTexturedIdentityShader = gltLoadShaderPairWithAttributes("TexturedIdentity.vp", "TexturedIdentity.fp", ,
        GLT_ATTRIBUTE_VERTEX, "vVertex", GLT_ATTRIBUTE_TEXTURE0, "vTexCoords");
    //生成紋理
    glGenTextures(, &textureID);
    //綁定個紋理
    glBindTexture(GL_TEXTURE_2D, textureID);
    //對GL_TEXTURE_2D的圖像進行加載
    LoadTGATexture("stone.tga", GL_LINEAR, GL_LINEAR, GL_CLAMP_TO_EDGE);
}


///////////////////////////////////////////////////////////////////////////////
// Cleanup
void ShutdownRC()
{
    glDeleteProgram(myTexturedIdentityShader);
    glDeleteTextures(, &textureID);
}


///////////////////////////////////////////////////////////////////////////////
// Called to draw scene
void RenderScene(void)
{
    // 清除緩沖區的資料
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

    //産生紋理渲染器
    glUseProgram(myTexturedIdentityShader);
    glBindTexture(GL_TEXTURE_2D, textureID);//再次綁定
    //設定渲染器程式當中的colormap
    GLint iTextureUniform = glGetUniformLocation(myTexturedIdentityShader, "colorMap");
    glUniform1i(iTextureUniform, );//設定為0,因為是第一張圖 

    triangleBatch.Draw();

    // Perform the buffer swap to display back buffer
    glutSwapBuffers();
}


///////////////////////////////////////////////////////////////////////////////
// Main entry point for GLUT based programs
int main(int argc, char* argv[])
{
    gltSetWorkingDirectory(argv[]);

    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL);
    glutInitWindowSize(, );
    glutCreateWindow("Textured Triangle");
    glutReshapeFunc(ChangeSize);
    glutDisplayFunc(RenderScene);

    GLenum err = glewInit();
    if (GLEW_OK != err) {
        fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err));
        return ;
    }

    SetupRC();

    glutMainLoop();

    ShutdownRC();

    return ;
}
           

最終的調用主程式,注意其中對紋理的調用和綁定。

(2)照亮紋理單元

在所有的光照着色器當中,我們實際上是将基本色值和光線的強度相乘,這可以是逐個頂點進行的,也可以是逐個像素進行的。

我們将修改過的ADSPhong着色器稱為ADSTexture

會對紋理進行采樣,然後用紋理的顔色值乘以光線強度。

正常來說,如果環境剛和漫反射的和可能會很亮,以至于看起來就是純白色,可是如果乘以一個紋理顔色,那麼結果就會是一個和原來紋理一樣亮度的紋理顔色。看起來沒什麼不對,但是:

注意,在光照計算的時候,每個顔色通道的值通常是大于1的,意味着隻收使得顔色過于飽和以及獲得一個白色鏡面高光是可能的,不過正确的方法應該是将環境光和漫反射光強度的和和紋理顔色相乘,最後加上鏡面光的部分。

// ADS Point lighting Shader
// Vertex Shader
// Richard S. Wright Jr.
// OpenGL SuperBible
#version 130

// 輸入每一個頂點以及法線向量
in vec4 vVertex;
in vec3 vNormal;
in vec4 vTexture0;

uniform mat4   mvpMatrix;
uniform mat4   mvMatrix;
uniform mat3   normalMatrix;
uniform vec3   vLightPosition;

// 仍然是ADSPhong 的套路,法線插值,把計算的重心轉移到片段着色器
smooth out vec3 vVaryingNormal;
smooth out vec3 vVaryingLightDir;
smooth out vec2 vTexCoords;

void main(void) 
    { 
    // 計算視覺坐标系當中的法向量
    vVaryingNormal = normalMatrix * vNormal;

    // 視覺坐标當中的頂點
    vec4 vPosition4 = mvMatrix * vVertex;
    vec3 vPosition3 = vPosition4.xyz / vPosition4.w;

    // 得到光的方向
    vVaryingLightDir = normalize(vLightPosition - vPosition3);

    // 傳遞貼圖坐标
    vTexCoords = vTexture0.st;

    // 最後把變換應用到幾何坐标上
    gl_Position = mvpMatrix * vVertex;
    }
           

頂點渲染器程式代碼,vp檔案。其中處理了視覺坐标系下的法向量、光照方向、以及傳遞貼圖坐标。

// ADS Point lighting Shader
// Fragment Shader
// Richard S. Wright Jr.
// OpenGL SuperBible
#version 130
//片段顔色
out vec4 vFragColor;

//得到三種光照顔色的統一值
uniform vec4      ambientColor;
uniform vec4      diffuseColor;   
uniform vec4      specularColor;
uniform sampler2D colorMap;//以及紋理取樣

//輸入的視覺坐标系當中的頂點法線向量
//光線向量
//紋理坐标
smooth in vec3 vVaryingNormal;
smooth in vec3 vVaryingLightDir;
smooth in vec2 vTexCoords;

void main(void)
    { 
    // 點乘得到漫反射光照強度
    float diff = max(, dot(normalize(vVaryingNormal), normalize(vVaryingLightDir)));

    // 光強乘以漫反射顔色,alpha通道預設為1
    vFragColor = diff * diffuseColor;

    // 在此基礎上加上環境光顔色
    vFragColor += ambientColor;

    // 将片段顔色和紋理進行乘運算
    vFragColor *= texture(colorMap, vTexCoords);

    // 最後進行鏡面光
    vec3 vReflection = normalize(reflect(-normalize(vVaryingLightDir), normalize(vVaryingNormal)));
    float spec = max(, dot(normalize(vVaryingNormal), vReflection));
    if(diff != ) {
        float fSpec = pow(spec, );
        vFragColor.rgb += vec3(fSpec, fSpec, fSpec);
        }
    }
           

在frag渲染器當中,注意,其渲染順序,首先是漫反射和環境光相加,然後和紋理相乘,最後計算鏡面光并且相加。

#pragma comment(lib,"GLTools.lib")
#include <GLTools.h>    // OpenGL toolkit
#include <GLMatrixStack.h>
#include <GLFrame.h>
#include <GLFrustum.h>
#include <GLGeometryTransform.h>
#include <StopWatch.h>

#include <math.h>

#include <GL/glut.h>


GLFrame             viewFrame;
GLFrustum           viewFrustum;
GLTriangleBatch     sphereBatch;
GLMatrixStack       modelViewMatrix;
GLMatrixStack       projectionMatrix;
GLGeometryTransform transformPipeline;
GLShaderManager     shaderManager;

GLuint  ADSTextureShader;   // The textured diffuse light shader
GLint   locAmbient;         // The location of the ambient color
GLint   locDiffuse;         // The location of the diffuse color
GLint   locSpecular;        // The location of the specular color
GLint   locLight;           // The location of the Light in eye coordinates
GLint   locMVP;             // The location of the ModelViewProjection matrix uniform
GLint   locMV;              // The location of the ModelView matrix uniform
GLint   locNM;              // The location of the Normal matrix uniform
GLint   locTexture;
GLuint  texture;

// 加載紋理函數
bool LoadTGATexture(const char *szFileName, GLenum minFilter, GLenum magFilter, GLenum wrapMode)
{
    GLbyte *pBits;
    int nWidth, nHeight, nComponents;
    GLenum eFormat;

    // Read the texture bits
    pBits = gltReadTGABits(szFileName, &nWidth, &nHeight, &nComponents, &eFormat);
    if (pBits == NULL)
        return false;

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapMode);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapMode);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magFilter);

    glPixelStorei(GL_UNPACK_ALIGNMENT, );
    glTexImage2D(GL_TEXTURE_2D, , nComponents, nWidth, nHeight, ,
        eFormat, GL_UNSIGNED_BYTE, pBits);

    free(pBits);

    if (minFilter == GL_LINEAR_MIPMAP_LINEAR ||
        minFilter == GL_LINEAR_MIPMAP_NEAREST ||
        minFilter == GL_NEAREST_MIPMAP_LINEAR ||
        minFilter == GL_NEAREST_MIPMAP_NEAREST)
        glGenerateMipmap(GL_TEXTURE_2D);

    return true;
}

// 初始化渲染環境
void SetupRC(void)
{
    // 1.渲染顔色
    glClearColor(, , , );

    //2.開啟渲染模式:深層檢測,遮擋剔除
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_CULL_FACE);

    //3.初始化渲染器
    shaderManager.InitializeStockShaders();

    //4.設定好錄影機的位置
    viewFrame.MoveForward();

    // 5.建立對象
    gltMakeSphere(sphereBatch, , , );

    //6.加載渲染器程式代碼
    ADSTextureShader = gltLoadShaderPairWithAttributes("ADSTexture.vp", "ADSTexture.fp", , GLT_ATTRIBUTE_VERTEX, "vVertex",
        GLT_ATTRIBUTE_NORMAL, "vNormal", GLT_ATTRIBUTE_TEXTURE0, "vTexture0");

    //7.得到對應統一值的位置并且做進一步修改
    locAmbient = glGetUniformLocation(ADSTextureShader, "ambientColor");
    locDiffuse = glGetUniformLocation(ADSTextureShader, "diffuseColor");
    locSpecular = glGetUniformLocation(ADSTextureShader, "specularColor");
    locLight = glGetUniformLocation(ADSTextureShader, "vLightPosition");
    locMVP = glGetUniformLocation(ADSTextureShader, "mvpMatrix");
    locMV = glGetUniformLocation(ADSTextureShader, "mvMatrix");
    locNM = glGetUniformLocation(ADSTextureShader, "normalMatrix");
    locTexture = glGetUniformLocation(ADSTextureShader, "colorMap");

    //8.生成紋理,并且綁定在一個宏上,并且從檔案中加載
    glGenTextures(, &texture);
    glBindTexture(GL_TEXTURE_2D, texture);
    LoadTGATexture("CoolTexture.tga", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR, GL_CLAMP_TO_EDGE);
}

// 清除
void ShutdownRC(void)
{
    //相當于析構函數
    glDeleteTextures(, &texture);
}


// 真正的渲染場景程式
void RenderScene(void)
{
    //1.設定計時器
    static CStopWatch rotTimer;

    // 2.清除顔色緩沖區,深度緩沖區
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    //3.對物體進行集合變換
    modelViewMatrix.PushMatrix(viewFrame);
    modelViewMatrix.Rotate(rotTimer.GetElapsedSeconds() * , , , );

    //4.寫死對應的屬性
    GLfloat vEyeLight[] = { -, ,  };
    GLfloat vAmbientColor[] = { , , ,  };
    GLfloat vDiffuseColor[] = { , , ,  };
    GLfloat vSpecularColor[] = { , , ,  };

    //綁定紋理
    glBindTexture(GL_TEXTURE_2D, texture);
    //激活渲染程式
    glUseProgram(ADSTextureShader);
    //對統一值進行指派
    glUniform4fv(locAmbient, , vAmbientColor);
    glUniform4fv(locDiffuse, , vDiffuseColor);
    glUniform4fv(locSpecular, , vSpecularColor);
    glUniform3fv(locLight, , vEyeLight);
    //設定模型投影矩陣,在頂點渲染器設定gl_position用到
    glUniformMatrix4fv(locMVP, , GL_FALSE, transformPipeline.GetModelViewProjectionMatrix());
    //模型矩陣,在頂點渲染器當中,計算視覺坐标系下的頂點坐标
    glUniformMatrix4fv(locMV, , GL_FALSE, transformPipeline.GetModelViewMatrix());
    //法線矩陣,在頂點着色器當中,計算視覺坐标系下的法線
    glUniformMatrix3fv(locNM, , GL_FALSE, transformPipeline.GetNormalMatrix());
    //設定紋理采樣單元
    glUniform1i(locTexture, );
    sphereBatch.Draw();

    modelViewMatrix.PopMatrix();

    glutSwapBuffers();
    glutPostRedisplay();
}



void ChangeSize(int w, int h)
{
    // Prevent a divide by zero
    if (h == )
        h = ;

    // Set Viewport to window dimensions
    glViewport(, , w, h);

    viewFrustum.SetPerspective(, float(w) / float(h), , );

    projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
    transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
}

///
// Main entry point for GLUT based programs
int main(int argc, char* argv[])
{
    gltSetWorkingDirectory(argv[]);

    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL);
    glutInitWindowSize(, );
    glutCreateWindow("Lit Texture");
    glutReshapeFunc(ChangeSize);
    glutDisplayFunc(RenderScene);

    GLenum err = glewInit();
    if (GLEW_OK != err) {
        fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err));
        return ;
    }

    SetupRC();
    glutMainLoop();
    ShutdownRC();
    return ;
}
           

這個主程式涉及了很多我們之前所提到的知識,我在每個語句上都加了注釋。

重要的額SetUpRC以及RenderScene這兩個函數,所執行的步驟,是我們目前為止所學的必要步驟。基本套路就是這樣,可以模仿這樣的步驟進行創造。

(3)丢棄片段

片段着色器設有取消處理過程而不寫入任何片段顔色值(或者是深度,模闆)的選項。聲明discard隻會使得片段程式停止運作,這個聲明的一個正常用途就是執行alpha測試。普通的混合操作需要從顔色緩沖區進行一次讀取,兩次乘法,對顔色進行一次求和。然後将會得到的值寫回顔色緩沖區。如果alpha值為0,或者非常接近0,那麼片段實際上是不可見的。

繪制不可見的片段是對性能的嚴重侮辱,這樣會在深度緩沖區建立一個不可見模式。進而導緻深度測試異常。alpha測試隻是檢查一些門檻值,并且在alpha值低于這個值的時候,完全丢棄這個片段。

if(vColorValue.a<f)
{
discard;
}
           

使用這種特性可以建立出非常棒的侵蝕着色器(erosion shader)。侵蝕着色器可以使幾何圖形看起來經過了歲月的侵蝕。使用discard聲明,我們可以逐個像素的控制哪個片段會進行繪制,哪個片段不會。

在用戶端代碼中,設定了一個基于時間的Uniform值,取值範圍為1-0,覆寫10s的範圍。最終結果是在10s内消散。

我們通過對雲紋理進行采樣,并且将一個顔色分量和倒計時變量進行比較,當顔色值大于門檻值的時候,則完全丢棄片段。

// ADS Point lighting Shader
// Vertex Shader
// Richard S. Wright Jr.
// OpenGL SuperBible
#version 130

// 輸入每個點,法向量,紋理坐标
in vec4 vVertex;
in vec3 vNormal;
in vec2 vTexCoords0;


uniform mat4   mvpMatrix;
uniform mat4   mvMatrix;
uniform mat3   normalMatrix;
uniform vec3   vLightPosition;

// 需要輸出到片段着色器當中做進一步計算。
smooth out vec3 vVaryingNormal;
smooth out vec3 vVaryingLightDir;
smooth out vec2 vVaryingTexCoord;


void main(void) 
    {
    // 傳遞紋理坐标
    vVaryingTexCoord = vTexCoords0;

    // 計算視覺坐标系當中的表面法向量
    vVaryingNormal = normalMatrix * vNormal;

    // 視覺坐标系的頂點位置
    vec4 vPosition4 = mvMatrix * vVertex;
    vec3 vPosition3 = vPosition4.xyz / vPosition4.w;

    // 光照方向
    vVaryingLightDir = normalize(vLightPosition - vPosition3);


    // 幾何變換
    gl_Position = mvpMatrix * vVertex;
    }
           

點渲染程式,和之前的程式并沒有差别。

// ADS Point lighting Shader
// Fragment Shader
// Richard S. Wright Jr.
// OpenGL SuperBible
#version 130

out vec4 vFragColor;

uniform vec4       ambientColor;
uniform vec4       diffuseColor;   
uniform vec4       specularColor;
uniform sampler2D  cloudTexture;
uniform float      dissolveFactor;

smooth in vec3 vVaryingNormal;
smooth in vec3 vVaryingLightDir;
smooth in vec2 vVaryingTexCoord;

void main(void)
    { 
    //注意,這是灰階圖,那麼貼圖顔色和光照顔色乘積就毫無意義。是以這裡并沒有乘
    vec4 vCloudSample = texture(cloudTexture, vVaryingTexCoord);

    //這種消散效果僅僅是對紋理取樣進行門檻值的驗證,如果符合那麼就丢棄
    if(vCloudSample.r < dissolveFactor)
        discard;



    float diff = max(, dot(normalize(vVaryingNormal), normalize(vVaryingLightDir)));

    vFragColor = diff * diffuseColor;


    vFragColor += ambientColor;



    vec3 vReflection = normalize(reflect(-normalize(vVaryingLightDir), normalize(vVaryingNormal)));
    float spec = max(, dot(normalize(vVaryingNormal), vReflection));
    if(diff != ) {
        float fSpec = pow(spec, );
        vFragColor.rgb += vec3(fSpec, fSpec, fSpec);
        }
    }
           

片段渲染器,僅僅多了一條門檻值驗證。

float fFactor = fmod(rotTimer.GetElapsedSeconds(), );
    fFactor /= ;
    glUniform1f(locDissolveFactor, fFactor);
    torusBatch.Draw();
           

在主程式的RenderScene函數中,通過設定門檻值的不斷變化,然後傳入着色器程式中。通過門檻值的不斷變化,來産生侵蝕的效果,耍了個小花招。

(4)卡通着色器:将紋理單元作為光源

所謂卡通着色,無非是一維貼圖紋理。通常應用在計算機遊戲中對一個幾何圖形進行渲染。使其看起來像是卡通片。

卡通着色,(Toon shader, cell shading)将一個一維紋理貼圖作為一個查詢表,使用紋理貼圖中的純色(GL_NEAREST)填充幾何圖形。

基本思路是:使用漫反射光照強度(視覺空間表面法線和光線方向的點乘積)作為紋理坐标添加到一個包含逐漸變亮顔色表的一維紋理中。

#version 130

// Incoming per vertex... position and normal
in vec4 vVertex;
in vec3 vNormal;

smooth out float textureCoordinate;

uniform vec3    vLightPosition;
uniform mat4    mvpMatrix;
uniform mat4    mvMatrix;
uniform mat3    normalMatrix;


void main(void) 
    { 
    // Get surface normal in eye coordinates
    vec3 vEyeNormal = normalMatrix * vNormal;

    // Get vertex position in eye coordinates
    vec4 vPosition4 = mvMatrix * vVertex;
    vec3 vPosition3 = vPosition4.xyz / vPosition4.w;

    // Get vector to light source
    vec3 vLightDir = normalize(vLightPosition - vPosition3);

    // 點乘積給我們漫反射強度,并且以此作為紋理坐标
    textureCoordinate = max(, dot(vEyeNormal, vLightDir));

    // Don't forget to transform the geometry!
    gl_Position = mvpMatrix * vVertex;
    }
           

這裡的頂點渲染器,需要的東西,注意紋理坐标的生成。

#version 130

uniform sampler1D colorTable;
out vec4 vFragColor;

smooth in float textureCoordinate;


void main(void)
   { 
   //注意,得到的紋理坐标,和colorTable進行綁定
   vFragColor = texture(colorTable, textureCoordinate);
   }
           

最後在片段着色器當中将紋理坐标和顔色表進行綁定。

glGenTextures(, &texture);
    glBindTexture(GL_TEXTURE_1D, texture);
    GLubyte textureData[][] = { ,  , ,
        ,  , ,
        , , ,
        , ,  };

    glTexImage1D(GL_TEXTURE_1D, , GL_RGB, , , GL_RGB, GL_UNSIGNED_BYTE, textureData);
    glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
           

在初始化渲染環境當中,注意初始化顔色表。

綁定這個顔色表,在之後的渲染都用這個進行渲染。

繼續閱讀