Cg_OpenGL Lighting

光照模型将采用广泛应用的phong模型,虽然这种模型在openGL的固定管线中已经实现了,但是学习该光照模型可以更加清楚的了解可编程渲染管线的流程。

 

首先要实现phong光照模型先要了解该模型中光照计算,关于phong模型光照计算的相关资料网上相当多,或者参考任何一本计算机图形学的相关书籍即可,这里只给出计算公式。

 

C = ambient + diffuse + specular

 

从公式中可以得出,物体顶点的最终颜色是由环境反射,漫反射和镜面反射三个成分来决定的。环境反射的计算公式为:

 

ambient = IaKa

 

其中,Ia是光的环境反射强度,Ka是物体材质的环境射系数。环境反射很简单,和光源、法线等等都没有关系,下面是只有环境反射的例子。

 

 

Fig1环境反射,Ia=(1.0, 1.0, 1.0)Ka=(0.15,0.15,0.0)

 

和环境反射相比,漫反射就稍微复杂一点,它和光源的位置和物体顶点的法线都有关系。漫反射的计算公式为:

 

diffuse = I­d­K(NL)

 

其中I­d­是光的漫反射强度,Kd是物体材质漫反射系数,NL表示法线N和入射光线L的内积。下面是只有面反射的例子。

 

 

Fig2漫反射,Id=(1.0, 1.0, 1.0)Kd=(1.0, 1.0 , 0.0)

 

最后是镜面反射,和漫反射相比,镜面反射又复杂了一点。它不仅和光源位置,物体顶点法线有关系,而且还和我们观看的位置有关系。镜面反射的计算公式为:

 

specular = IsKs(VR)­n

 

其中Is是光的镜面反射强度,Ks是物体材质的镜面反射系数,VR表示相机朝向向量V和反射光线R的内积,n表示该内积的n次幂。这里反射光线R可以通过公式

 

= 2(LN)N-L­

 

来得到。这里还可以使用half vector来计算,计算half vector要比计算反射向量R方便快速的多。

 

H=(L+V)/2

 

所以,镜面反射公式现在可以写成

 

specular = IsKs(NH)­n

 

下面是只有镜面反射的例子。

 

 

 

Fig3镜面反射,Is=(1.0, 1.0, 1.0)Ks=(1.0, 1.0 , 1.0,), n=32

通过上面的过程,分别计算出了物体每个顶点的环境反射,面反射和镜面反射。最后简单将这三个成分相加即可得到顶点最终的颜色。

 

C = IaKa + I­d­K(NL)IsKs(NH)­n

 

图fig4显示了这个相加的过程。

 

+

+

=

Fig4 phong光照

 

下面是vertex shader的代码。由于是基于vertex的光照,所以不需要fragment shader。

vertex脚本:

uniform float3 LightPosition; //光源位置
uniform float3 eyePosition;   //相机位置
uniform float3 I;              //光强度
uniform float3 Ka;             //环境光反射系数
uniform float3 Kd;             //漫反射系数
uniform float3 Ks;             //镜面反射系数
uniform float shininess;      //n幂次
 
struct output
{
      float4 position : POSITION; 
      float4 color     : COLOR; 
};
 
output v_main( float4 position : POSITION,
                     float3 normal   : NORMAL,
                     uniform float4x4 MV, // 在相机座标系中计算,所以要用到ModelView变换矩阵
                     uniform float4x4 MVP // ModelViewProjection变换矩阵
                    )
{
      output OUT;
      OUT.position = mul(MVP, position);
 
      float3 N = normalize(mul(MV, float4(normal,0.0)) ).xyz; //转换法线到相机座标系
      float3 P = mul(MV, position).xyz; //转换物体顶点到相机座标系
 
      float3 L = normalize(LightPosition - P);
      float NdotL = max(dot(N,L),0); //判断法线和入射光线的角度是否大于90度
 
      float3 ambient = Ka * I; //环境反射
      float3 diffuse = Kd * I * NdotL; //漫反射
 
      float3 V = normalize(eyePosition - P);
      float3 H = normalize(L+V); //half vector
      float NdotH = pow(max(dot(N,H), 0), shininess);
      if(NdotL<=0) NdotH = 0.0;
 
      float3 specular = Ks*I*NdotH; //镜面反射
 
      float3 color = ambient + diffuse +specular; //所有成分相加
      OUT.color.xyz= color;
      OUT.color.w = 1.0;
 
      return OUT;
}


主程序:

#include <gl/glut.h>
#include <cg/cg.h>
#include <Cg/cgGL.h>
#include <stdio.h>
#include <Windows.h>

int ww = 640, hh = 480;

void render();
void reshape(int w, int h);

static CGcontext   myCgContext;
static CGprofile   myCgVertexProfile;
static CGprogram   myCgVertexProgram;

static const char *myProgramName = "Lighting CG";
static const char *myVertexProgramFileName = "CSDN_02v.cg";
static const char *myVertexProgramName = "v_main";


CGparameter lp, ep, i, a, d, s, n;
//display FPS on the title of the window
static float lastTime   = 0.0f;
void displayFPS(){
	static float framesPerSecond    = 0.0f;       // This will store our fps
	// This will hold the time from the last frame
	float currentTime = GetTickCount() * 0.001f;    
	if( currentTime - lastTime > 0.0f )
	{
		framesPerSecond = 1/(currentTime - lastTime);
		char strFrameRate[256];
		lastTime = currentTime;
		sprintf(strFrameRate, "Current Frames Per Second: %f", framesPerSecond);
		glutSetWindowTitle( strFrameRate);
		framesPerSecond = 0;
	}
}


int main(int argc, char** argv)
{
	//【1】初始化部分
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
	glutInitWindowSize(ww,hh);
	glutCreateWindow(myProgramName);

	//【2】构建shader运行环境;
	myCgContext = cgCreateContext();
	cgGLSetDebugMode(CG_FALSE);
	cgSetParameterSettingMode(myCgContext, CG_DEFERRED_PARAMETER_SETTING);

	myCgVertexProfile = cgGLGetLatestProfile(CG_GL_VERTEX);
	cgGLSetOptimalOptions(myCgVertexProfile);

	myCgVertexProgram =
		cgCreateProgramFromFile(
		myCgContext,              /* Cg runtime context */
		CG_SOURCE,                /* Program in human-readable form */
		myVertexProgramFileName,  /* Name of file containing program */
		myCgVertexProfile,        /* Profile: OpenGL ARB vertex program */
		myVertexProgramName,      /* Entry function name */
		NULL);                    /* No extra compiler options */

	cgGLLoadProgram(myCgVertexProgram);


	//【3】将shader载入,并运行
	glutDisplayFunc(render);
	glutReshapeFunc(reshape);

	glEnable(GL_DEPTH_TEST);



	glutMainLoop();
	return 0;
}

void reshape(int w, int h)
{
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

	gluPerspective(45, (float)w/(float)h, 0.1, 100);
	glViewport(0,0,w,h);

	ww = w;
	hh = h;
}

void render()
{
	displayFPS();
	//【3.1】gl视图变换
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glClearColor(.0f, .0f, .2f, 1.0f);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	gluLookAt(.0,.0,5.0, .0,.0,.0, .0,1.0,.0);

	static float angle;
	glRotatef(angle, 0.0,1.0,0.0);

	//【3.2】把程序与当前API状态绑定起来;
	cgGLBindProgram(myCgVertexProgram);
	cgGLEnableProfile(myCgVertexProfile);

	//将ModelViewProjection矩阵传入shader
	CGparameter mvp = cgGetNamedParameter(myCgVertexProgram, "MVP");
	cgGLSetStateMatrixParameter(mvp, CG_GL_MODELVIEW_PROJECTION_MATRIX, CG_GL_MATRIX_IDENTITY);
	
	CGparameter mv = cgGetNamedParameter(myCgVertexProgram, "MV");
	cgGLSetStateMatrixParameter(mv, CG_GL_MODELVIEW_MATRIX, CG_GL_MATRIX_IDENTITY);
	//注意传参的方法☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆
	
	lp = cgGetNamedParameter(myCgVertexProgram, "LightPosition");
	ep = cgGetNamedParameter(myCgVertexProgram, "eyePosition");
	i = cgGetNamedParameter(myCgVertexProgram, "I");
	a = cgGetNamedParameter(myCgVertexProgram, "Ka");
	d = cgGetNamedParameter(myCgVertexProgram, "Kd");
	s = cgGetNamedParameter(myCgVertexProgram, "Ks");
	n = cgGetNamedParameter(myCgVertexProgram, "shininess");

	cgSetParameter3f(lp,1.0,1.0,1.0);
	cgSetParameter3f(ep,0,0.5,0);
	cgSetParameter3f(i ,1.0, 1.0, 1.0);
	cgSetParameter3f(a ,0.15,0.15, 0.0);
	cgSetParameter3f(d ,1.0, 1.0 , 0.0);
	cgSetParameter3f(s ,1.0, 1.0 , 1.0);
	cgSetParameter1f(n ,32);


	glutSolidTorus(0.3,1.0,30,30);

	cgGLDisableProfile(myCgVertexProfile);

	angle += 0.5;
	if(angle >=360) angle = 0.0f;

	glutSwapBuffers();
	glutPostRedisplay();
}



由于是基于vertex的光照,虽然采用gouraud shading要比flat shading效果好的多,但是和phong shading的效果相差很大。这里大家要注意的是,phong modelphong shading的区别。上一篇教程所讲的光照模型叫phong model,而这篇教程要介绍的一种着色方法叫phong shading,必须要使用shader才能实现。下面的图中对比了采用flat shadinggouraud shadingphong shading技术渲染的一个圆环。

  

Flat shading

Gouraud shading

Phong shading


Cg来实现基于pixel lighingphong shading光照就容易多了。绝大部分的Cg shader代码都是一样的,主要的改变就是这次不是在vertex shader里光照,而是在fragment shader里计算光照。所以整个vertex shader的代码很简单,将要渲染的物体的顶点位置,顶点法线传入fragment shader就是vertex shader的全部工作。整个vertex shader的代码如下。

  

上面的代码的输出结构体中有三个成员。Position是传入的物体的顶点,该顶点将用ModelViewProjection矩阵转换成剪裁座标系中的座标供光栅化使用。一旦将座标转换后,我们就无法在fragment shader中使用物体的顶点座标了。由于要在fragmentshader中计算光照,所以我们要将物体的顶点位置,顶点法线都传入到fragment shader中。这里物体转换前的顶点和法线分别使用了语义TEXCOORD0TEXCOORD1,代表将它们作为纹理座标后传入fragment shader。这样GPU会把顶点和法线信息当做纹理座标来处理,在贴图的时候,GPU会根据纹理自动插值计算每个像素对应的颜色,而现在GPU插值计算出来的就是每个像素对应的座标和法线信息了。有了这些数据就可以进行phong shading的计算了。光照计算的公式和方法和上一个教程介绍的一模一样。

vertex脚本:

struct output
{
      float4 position  : POSITION;    
      float3 objectPos : TEXCOORD0;   
      float3 normal     : TEXCOORD1;
};
 
output v_main( float4 position : POSITION,
                  float3 normal   : NORMAL,
                  uniform float4x4 MV,
                  uniform float4x4 MVP
                    )
{
      output OUT;
      OUT.position = mul(MVP, position);
      OUT.objectPos = mul(MV, position).xyz;
      OUT.normal = mul(MV, float4(normal,0.0)).xyz;
 
      return OUT;
}


fragment脚本:

uniform float3 LightPosition;
uniform float3 eyePosition;
uniform float3 I;
uniform float3 Ka;
uniform float3 Kd;
uniform float3 Ks;
uniform float shininess;
 
struct input{
      float3 objectPos: TEXCOORD0;   
      float3 normal   : TEXCOORD1;
};
 
struct output{
      float4 color     : COLOR;
};
 
output f_main( in input IN )
{
      output OUT;
 
      float3 N = normalize(IN.normal);
      float3 P = IN.objectPos;
 
      float3 L = normalize(LightPosition - P);
      float NdotL = max(dot(N,L),0);
 
      float3 ambient = Ka * I;
      float3 diffuse = Kd * I * NdotL;
 
      float3 V = normalize(eyePosition - P);
      float3 H = normalize(L+V);
      float NdotH = pow(max(dot(N,H), 0), shininess);
 
      if(NdotL<=0)
           NdotH = 0.0;
      float3 specular = Ks*I*NdotH;
 
      float3 color = ambient + diffuse + specular;
      OUT.color.xyz= color;
      OUT.color.w = 1.0;
 
      return OUT;
}


主程序:

#include <gl/glut.h>
#include <cg/cg.h>
#include <Cg/cgGL.h>
#include <stdio.h>
#include <Windows.h>


int ww = 640, hh = 480;


void render();
void reshape(int w, int h);


static CGcontext   myCgContext;
static CGprofile   myCgVertexProfile;
static CGprofile   myCgFragmentProfile;
static CGprogram   myCgVertexProgram;
static CGprogram   myCgFragmentProgram;


static const char *myProgramName = "Lighting CG2";
static const char *myVertexProgramFileName = "CSDN_03v.cg";
static const char *myVertexProgramName = "v_main";
static const char *myFragmentProgramFileName = "CSDN_03f.cg";
static const char *myFragmentProgramName = "f_main";


CGparameter lp, ep, i, a, d, s, n;
//display FPS on the title of the window




void displayFPS(){
	static float lastTime   = 0.0f;
	static float framesPerSecond    = 0.0f;       // This will store our fps
	// This will hold the time from the last frame
	float currentTime = GetTickCount() * 0.001f;    
	if( currentTime - lastTime > 0.0f )
	{
		framesPerSecond = 1/(currentTime - lastTime);
		char strFrameRate[256];
		lastTime = currentTime;
		sprintf(strFrameRate, "Current Frames Per Second: %f", framesPerSecond);
		glutSetWindowTitle( strFrameRate);
		framesPerSecond = 0;
	}
}




int main(int argc, char** argv)
{
	//【1】初始化部分
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
	glutInitWindowSize(ww,hh);
	glutCreateWindow(myProgramName);


	//【2】构建shader运行环境;
	myCgContext = cgCreateContext();
	cgGLSetDebugMode(CG_FALSE);
	cgSetParameterSettingMode(myCgContext, CG_DEFERRED_PARAMETER_SETTING);


	myCgVertexProfile = cgGLGetLatestProfile(CG_GL_VERTEX);
	cgGLSetOptimalOptions(myCgVertexProfile);


	myCgVertexProgram =
		cgCreateProgramFromFile(
		myCgContext,              /* Cg runtime context */
		CG_SOURCE,                /* Program in human-readable form */
		myVertexProgramFileName,  /* Name of file containing program */
		myCgVertexProfile,        /* Profile: OpenGL ARB vertex program */
		myVertexProgramName,      /* Entry function name */
		NULL);                    /* No extra compiler options */
	cgGLLoadProgram(myCgVertexProgram);


	myCgFragmentProfile = cgGLGetLatestProfile(CG_GL_FRAGMENT);
	cgGLSetOptimalOptions(myCgFragmentProfile);


	myCgFragmentProgram =
		cgCreateProgramFromFile(
		myCgContext,                /* Cg runtime context */
		CG_SOURCE,                  /* Program in human-readable form */
		myFragmentProgramFileName,  /* Name of file containing program */
		myCgFragmentProfile,        /* Profile: OpenGL ARB vertex program */
		myFragmentProgramName,      /* Entry function name */
		NULL);                      /* No extra compiler options */
	cgGLLoadProgram(myCgFragmentProgram);
	
	


	//【3】将shader载入,并运行
	glutDisplayFunc(render);
	glutReshapeFunc(reshape);


	glEnable(GL_DEPTH_TEST);






	glutMainLoop();
	return 0;
}


void reshape(int w, int h)
{
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();


	gluPerspective(45, (float)w/(float)h, 0.1, 100);
	glViewport(0,0,w,h);


	ww = w;
	hh = h;
}


void render()
{
	displayFPS();
	//【3.1】gl视图变换
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glClearColor(.0f, .0f, .2f, 1.0f);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	gluLookAt(.0,.0,5.0, .0,.0,.0, .0,1.0,.0);


	static float angle;
	glRotatef(angle, 0.0,1.0,0.0);


	//【3.2】把程序与当前API状态绑定起来;
	cgGLBindProgram(myCgVertexProgram);
	cgGLEnableProfile(myCgVertexProfile);
	cgGLBindProgram(myCgFragmentProgram);
	cgGLEnableProfile(myCgFragmentProfile);


	//将ModelViewProjection矩阵传入shader
	CGparameter mvp = cgGetNamedParameter(myCgVertexProgram, "MVP");
	cgGLSetStateMatrixParameter(mvp, CG_GL_MODELVIEW_PROJECTION_MATRIX, CG_GL_MATRIX_IDENTITY);
	
	CGparameter mv = cgGetNamedParameter(myCgVertexProgram, "MV");
	cgGLSetStateMatrixParameter(mv, CG_GL_MODELVIEW_MATRIX, CG_GL_MATRIX_IDENTITY);
	//传参的方法


	lp = cgGetNamedParameter(myCgFragmentProgram, "LightPosition");
	ep = cgGetNamedParameter(myCgFragmentProgram, "eyePosition");
	i = cgGetNamedParameter(myCgFragmentProgram, "I");
	a = cgGetNamedParameter(myCgFragmentProgram, "Ka");
	d = cgGetNamedParameter(myCgFragmentProgram, "Kd");
	s = cgGetNamedParameter(myCgFragmentProgram, "Ks");
	n = cgGetNamedParameter(myCgFragmentProgram, "shininess");


	cgSetParameter3f(lp,1.0,1.0,1.0);
	cgSetParameter3f(ep,0,0.5,0);
	cgSetParameter3f(i ,1.0, 1.0, 1.0);
	cgSetParameter3f(a ,0.15,0.15, 0.0);
	cgSetParameter3f(d ,1.0, 1.0 , 0.0);
	cgSetParameter3f(s ,1.0, 1.0 , 1.0);
	cgSetParameter1f(n ,32);




	glutSolidTorus(0.3,1.0,30,30);


	cgGLDisableProfile(myCgVertexProfile);
	cgGLDisableProfile(myCgFragmentProfile);


	angle += 0.5;
	if(angle >=360) angle = 0.0f;


	glutSwapBuffers();
	glutPostRedisplay();
}


phong model结果1:                                                                                                    phong shading结果2:

                                             


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章