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,可以逐个像素地控制哪个片段进行绘制,哪个片段不会。

云雾的纹理图:




评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值