OpenGL蓝宝书源码学习(二十)第六章——Dissolve

侵蚀着色器渲染图元,呈现腐蚀效果的源码示例。

// Dissolve.cpp
// OpenGL SuperBible
// Demonstrates discard fragment command
// Program by Richard S. Wright Jr.

#include 
     
     
      
      	// OpenGL toolkit
#include 
      
      
       
       
#include 
       
       
        
        
#include 
        
        
         
         
#include 
         
         
           #include 
          
            #include 
           
             #ifdef __APPLE__ #include 
            
              #else #define FREEGLUT_STATIC #include 
             
               #endif GLFrame viewFrame; GLFrustum viewFrustum; GLTriangleBatch torusBatch; GLMatrixStack modelViewMatrix; GLMatrixStack projectionMatrix; GLGeometryTransform transformPipeline; GLShaderManager shaderManager; GLuint ADSDissloveShader; // The dissolving 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; // The location of the texture uniform GLint locDissolveFactor; // The location of the dissolve factor GLuint cloudTexture; // The cloud texture texture object // Load a TGA as a 2D Texture. Completely initialize the state 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, 1); glTexImage2D(GL_TEXTURE_2D, 0, nComponents, nWidth, nHeight, 0, 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; } // This function does any needed initialization on the rendering // context. void SetupRC(void) { // Background glClearColor(0.0f, 0.0f, 0.0f, 1.0f ); glEnable(GL_DEPTH_TEST); shaderManager.InitializeStockShaders(); viewFrame.MoveForward(4.0f); // Make the torus gltMakeTorus(torusBatch, .80f, 0.25f, 52, 26); ADSDissloveShader = gltLoadShaderPairWithAttributes("Dissolve.vp", "Dissolve.fp", 3, GLT_ATTRIBUTE_VERTEX, "vVertex", GLT_ATTRIBUTE_NORMAL, "vNormal", GLT_ATTRIBUTE_TEXTURE0, "vTexCoords0"); locAmbient = glGetUniformLocation(ADSDissloveShader, "ambientColor"); locDiffuse = glGetUniformLocation(ADSDissloveShader, "diffuseColor"); locSpecular = glGetUniformLocation(ADSDissloveShader, "specularColor"); locLight = glGetUniformLocation(ADSDissloveShader, "vLightPosition"); locMVP = glGetUniformLocation(ADSDissloveShader, "mvpMatrix"); locMV = glGetUniformLocation(ADSDissloveShader, "mvMatrix"); locNM = glGetUniformLocation(ADSDissloveShader, "normalMatrix"); locTexture = glGetUniformLocation(ADSDissloveShader, "cloudTexture"); locDissolveFactor = glGetUniformLocation(ADSDissloveShader, "dissolveFactor"); glGenTextures(1, &cloudTexture); glBindTexture(GL_TEXTURE_1D, cloudTexture); LoadTGATexture("Clouds.tga", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR, GL_CLAMP_TO_EDGE); } // Cleanup void ShutdownRC(void) { } // Called to draw scene void RenderScene(void) { static CStopWatch rotTimer; // Clear the window and the depth buffer glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); modelViewMatrix.PushMatrix(viewFrame); modelViewMatrix.Rotate(rotTimer.GetElapsedSeconds() * 10.0f, 0.0f, 1.0f, 0.0f); GLfloat vEyeLight[] = { -100.0f, 100.0f, 100.0f }; GLfloat vAmbientColor[] = { 0.1f, 0.1f, 0.1f, 1.0f }; GLfloat vDiffuseColor[] = { 0.1f, 1.0f, 0.1f, 1.0f }; GLfloat vSpecularColor[] = { 1.0f, 1.0f, 1.0f, 1.0f }; glUseProgram(ADSDissloveShader); glUniform4fv(locAmbient, 1, vAmbientColor); glUniform4fv(locDiffuse, 1, vDiffuseColor); glUniform4fv(locSpecular, 1, vSpecularColor); glUniform3fv(locLight, 1, vEyeLight); glUniformMatrix4fv(locMVP, 1, GL_FALSE, transformPipeline.GetModelViewProjectionMatrix()); glUniformMatrix4fv(locMV, 1, GL_FALSE, transformPipeline.GetModelViewMatrix()); glUniformMatrix3fv(locNM, 1, GL_FALSE, transformPipeline.GetNormalMatrix()); glUniform1i(locTexture, 1); float fFactor = fmod(rotTimer.GetElapsedSeconds(), 10.0f); fFactor /= 10.0f; glUniform1f(locDissolveFactor, fFactor); torusBatch.Draw(); modelViewMatrix.PopMatrix(); glutSwapBuffers(); glutPostRedisplay(); } void ChangeSize(int w, int h) { // Prevent a divide by zero if(h == 0) h = 1; // Set Viewport to window dimensions glViewport(0, 0, w, h); viewFrustum.SetPerspective(35.0f, float(w)/float(h), 1.0f, 100.0f); projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix()); transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix); } /// // Main entry point for GLUT based programs int main(int argc, char* argv[]) { gltSetWorkingDirectory(argv[0]); glutInit(&argc, argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL); glutInitWindowSize(800, 600); glutCreateWindow("Help me, I'm melting!"); glutReshapeFunc(ChangeSize); glutDisplayFunc(RenderScene); GLenum err = glewInit(); if (GLEW_OK != err) { fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err)); return 1; } SetupRC(); glutMainLoop(); ShutdownRC(); return 0; } // ADS Point lighting Shader // Vertex Shader // Richard S. Wright Jr. // OpenGL SuperBible #version 130 // Incoming per vertex... position and normal in vec4 vVertex; in vec3 vNormal; in vec2 vTexCoords0; uniform mat4 mvpMatrix; uniform mat4 mvMatrix; uniform mat3 normalMatrix; uniform vec3 vLightPosition; // Color to fragment program smooth out vec3 vVaryingNormal; smooth out vec3 vVaryingLightDir; smooth out vec2 vVaryingTexCoord; void main(void) { // Pass on the texture coordinates vVaryingTexCoord = vTexCoords0; // Get surface normal in eye coordinates vVaryingNormal = normalMatrix * vNormal; // Get vertex position in eye coordinates vec4 vPosition4 = mvMatrix * vVertex; vec3 vPosition3 = vPosition4.xyz / vPosition4.w; // Get vector to light source vVaryingLightDir = normalize(vLightPosition - vPosition3); // Don't forget to transform the geometry! 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; // Dot product gives us diffuse intensity float diff = max(0.0, dot(normalize(vVaryingNormal), normalize(vVaryingLightDir))); // Multiply intensity by diffuse color, force alpha to 1.0 vFragColor = diff * diffuseColor; // Add in ambient light vFragColor += ambientColor; // Specular Light vec3 vReflection = normalize(reflect(-normalize(vVaryingLightDir), normalize(vVaryingNormal))); float spec = max(0.0, dot(normalize(vVaryingNormal), vReflection)); if(diff != 0) { float fSpec = pow(spec, 128.0); vFragColor.rgb += vec3(fSpec, fSpec, fSpec); } } 
              
             
            
           
         
        
        
       
       
      
      
     
     



一、丢弃片段

片段着色器设有取消处理而不写入任何片段颜色(或者是深度、魔板等)值的选项。声明discard只会使片段程序停止运行,这个声明的一个常规用途就是执行alpha测试。普通的混合操作需要从颜色缓冲区进行一次读取、两次乘法(至少)、对颜色进行一次求和,然后将得到的值写回颜色缓冲区。如果alpha为0,或者非常接近0,那么片段实际上是不可见的。使用discard声明,我们可以逐个像素地控制哪个片段会进行绘制,哪个片段不会。

本次示例的目标是让对象(一个绿色花托)在10秒范围内“消散”。通过对云纹理进行采样,并将一个颜色分量与倒计时变量进行比较,当颜色值大于阈值时则完全丢弃片段。

二、源码解析

1、Client程序部分源码解析

1)变量

GLintlocTexture;// 返回获取到的片段着色器的采样器,代表采样的纹理所绑定的纹理单元。
GLint   locDissolveFactor;  // 返回获取到的片段着色器的腐蚀系数,与片段着色器对纹理进行采样的值进行比较,比较后丢弃片段

GLuintcloudTexture;// 返回获取到的纹理单元对象

注意:以上的声明的变量,都与着色器中统一值(uinform)相对应。

2)函数解析

1.void SetupRC(void)

...........

//加载着色器。设置顶点和片段着色器程序,和在顶点着色器中需要的3个属性,即顶点、法向量和纹理。

ADSDissloveShader = gltLoadShaderPairWithAttributes("Dissolve.vp", "Dissolve.fp", 3, GLT_ATTRIBUTE_VERTEX, "vVertex",
GLT_ATTRIBUTE_NORMAL, "vNormal", GLT_ATTRIBUTE_TEXTURE0, "vTexCoords0");

...............

//获取着色器中的cloudTexture统一值

locTexture = glGetUniformLocation(ADSDissloveShader, "cloudTexture");

//获取着色器中的dissolveFactor统一值
locDissolveFactor = glGetUniformLocation(ADSDissloveShader, "dissolveFactor");

//生成纹理单元

glGenTextures(1, &cloudTexture);

//绑定纹理单元
glBindTexture(GL_TEXTURE_1D, cloudTexture);

//选择载入纹理贴图。设置扩展和收缩的方式为线性,设置边缘环绕模式。
LoadTGATexture("Clouds.tga", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR, GL_CLAMP_TO_EDGE);


2.void RenderScene(void)

//定义一个使用时间的对象

static CStopWatch rotTimer;

...........

glUniform1i(locTexture, 0);  //设置着色器中的纹理单元统一值,使用默认标识符绑定的纹理单元。注意此示例只绑定一个纹理单元,第二参数任意整数

//fmod函数对浮点类型数据进行取模运算,即“参数1”%10。

float fFactor = fmod(rotTimer.GetElapsedSeconds(), 10.0f);
fFactor /= 10.0f;
glUniform1f(locDissolveFactor, fFactor); //设置着色器中的侵蚀系数
    torusBatch.Draw();    //渲染图元

.................

2、Server程序源码解析

Dissolve.vp

1)变量

//三个输入属性,顶点位置、法线和纹理坐标。

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;//纹理坐标

2)函数

void main(void) 

//用顶点着色器的纹理坐标设置得到纹理坐标

vVaryingTexCoord = vTexCoords0;

//用法向量矩阵乘以法线(输入属性)得到视坐标的各个顶点的法向量

vVaryingNormal = normalMatrix * vNormal;

//得到视坐标系中顶点的位置坐标。用模型视图矩阵乘以顶点坐标再除以w分量。

vec4 vPosition4 = mvMatrix * vVertex;
    vec3 vPosition3 = vPosition4.xyz / vPosition4.w;

//得到的光源的方向向量。用光源的位置坐标减去顶点的坐标,在进行归一化。

vVaryingLightDir = normalize(vLightPosition - vPosition3);

//几何渲染设置

gl_Position = mvpMatrix * vVertex;

Dissolve.fp

1)变量

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

2)函数

void main(void)

//使用纹理坐标(参数2)对纹理单元进行采样(参数1)得到颜色值(返回值)

vec4 vCloudSample = texture(cloudTexture, vVaryingTexCoord);

//判断如果纹理的alpha值小鱼腐蚀系数,则丢弃片段。

if(vCloudSample.r < dissolveFactor)
        discard;

//求光源照射在图元上的漫射光强度。法向量和光源方向向量的点乘积与0.0的最大值

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

//片段着色器输出颜色值为漫射光强度乘以漫射光颜色(漫射光颜色在RenderScene函数进行设置)

vFragColor = diff * diffuseColor;

//片段着色器输出颜色值加等环境光颜色值

vFragColor += ambientColor;

//先求反射光向量,根据射向光源方向的向量为入射向量和法向量求反射向量。再根据光源方向向量和反射向量的点乘积求镜面光强度,判断如果漫射光强度不是0,再进行128次幂。最后着色器输出颜色值加等镜面光强度颜色。

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

三、小结

此示例通过一个寝室的效果,简单学习了一下着色器中的丢弃片段的应用。通过加载一个云雾的纹理贴图获取其alpha值,在Client程序中设置一个腐蚀系数与纹理贴图的alpha值进行比较,当alpha的值小于腐蚀系数时,完全丢弃片段(discard)。腐蚀系数是在Client程序中随系统时间进行设置的,所以在着色器的丢弃片段时,采样得到的纹理颜色的alpha值与变化的腐蚀系数进行比较,逐个像素地控制绘制效果,会出现图形被侵蚀的效果。声明discard,可以逐个像素地控制哪个片段进行绘制,哪个片段不会。

云雾的纹理图:




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值