光照阴影镜面反射综合算法

时间:2022-08-06 04:13:24

#pragma once
#include <windows.h>
#include "math_3d.h"
#define GLUT_DISABLE_ATEXIT_HACK
#include <gl/glew.h>/*GL_BGR*/
#include <gl/glut.h>
#include <atlimage.h>


// 全局变量光照参数
float  ambientLight[] = { 0.3f, 0.3f, 0.3f, 1.0f };/*环境光参数*/
float  diffuseLight[] = { 1.0f, 1.0f, 1.0f, 1.0f };/*漫反射光参数*/
float  specular[] = { 1.0f, 1.0f, 1.0f, 1.0f };/*镜面反射光参数*/
float  specref[] = { 1.0f, 1.0f, 1.0f, 1.0f };/*材质镜面反射颜色属性*/

float  lightPos[] = { 8.0f, 10.0f, 4.0f, 1.0f };/*光源位置*/
//如果我们把lightPos数组的最后一个值设为0.0,
//可以使光源看上去像是来自无限远处,并沿着向量(X,Y,Z)所指定的方向射入。
//这种看似来自无限远处的光源称为方向性光源,它会均匀地照射在物体的表面,
//它的所有光线都是平行的, 是一种平行光。
//反之,对于位置性光源,光线会从光源处向不同方向发散开来。

float spotDir[] = { 0.0f, 0.0f, -1.0f };//聚光灯参数

//全局变量设置
// 旋转变量
static float g_xRot = 0.0f;
static float g_yRot = 0.0f;

float g_lightPos[] = { 4.0f, 4.0f, 2.0f, 1.0f };
float g_ambientLight[] = { 0.0f, 0.0f, 1.0f, 1.0f };
float g_cameraX = 0.0, g_cameraY = 0.0, g_cameraZ = 40.0;



unsigned int ATLLoadTexture(const char* fileName);
unsigned int g_groundtex;
unsigned int g_mirrorTex;


void InitTexture()
{
	g_groundtex = ATLLoadTexture("data/ground.jpg");
	g_mirrorTex = ATLLoadTexture("data/mirror.jpg");
}



//加载jpg/bmp等各种纹理返回纹理id
unsigned int ATLLoadTexture(const char* fileName)
{
	BITMAP bm;
	GLuint idTexture = 0;
	CImage img;             //需要头文件atlimage.h  
	HRESULT hr = img.Load(fileName);
	if (!SUCCEEDED(hr))   //文件加载失败  
	{
		MessageBox(NULL, "文件加载失败", "ERROR", 0);
		return NULL;
	}
	HBITMAP hbmp = img;
	if (!GetObject(hbmp, sizeof(bm), &bm))
		return 0;

	glGenTextures(1, &idTexture);
	if (idTexture)
	{
		glBindTexture(GL_TEXTURE_2D, idTexture);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
		glPixelStoref(GL_PACK_ALIGNMENT, 1);
		glTexImage2D(GL_TEXTURE_2D, 0, 3, bm.bmWidth, bm.bmHeight, 0, GL_BGR, GL_UNSIGNED_BYTE, bm.bmBits); //这里不是GL_RGB  
	}
	return idTexture;
}

// 声明一个阴影转换矩阵
M3DMatrix44f shadowMat;



//截图效果
<img src="https://img-blog.csdn.net/20151021183128451?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="" />

////////////////////////////////////////////////
// 绘制光照物以及影子
void DrawaScenes(int nShadow)
{

	glPushAttrib(GL_CURRENT_BIT);
	// 设置材质的颜色这里我们只需要设置为黑色表示阴影
	if (nShadow == 0)
		glColor3f(1.0, 1.0, 1.0);/*非阴影时颜色设置为白色等*/
	else
	{
		glColor3f(0.1, 0.1, 0.1);/*阴影时颜色设置为黑色*/
		/*绘制还要镜面反射的球体阴影*/
		glPushMatrix();
		glPushAttrib(GL_CURRENT_BIT);
		
		glTranslatef(g_yRot, g_xRot, 4);//物体位置
		glutSolidSphere(0.3, 20, 20);
		glPopMatrix();
		glPopAttrib();
	}
	/*绘制实体与阴影*/
	glPushMatrix();
	glTranslatef(-10.0, -5.0, 0.0);
	glScalef(0.5, 0.5, 0.5);
	glBegin(GL_QUADS);							        // 开始绘制四边形
	// 前侧面
	glNormal3f(0.0f, 0.0f, 1.0f);					    // 法线指向观察者
	glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f);
	glTexCoord2f(1.0f, 0.0f); glVertex3f(1.0f, -1.0f, 1.0f);
	glTexCoord2f(1.0f, 1.0f); glVertex3f(1.0f, 1.0f, 1.0f);
	glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f);
	// 后侧面
	glNormal3f(0.0f, 0.0f, -1.0f);					    // 法线背向观察者
	glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f);
	glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f);
	glTexCoord2f(0.0f, 1.0f); glVertex3f(1.0f, 1.0f, -1.0f);
	glTexCoord2f(0.0f, 0.0f); glVertex3f(1.0f, -1.0f, -1.0f);
	// 顶面
	glNormal3f(0.0f, 1.0f, 0.0f);					    // 法线向上
	glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f);
	glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, 1.0f, 1.0f);
	glTexCoord2f(1.0f, 0.0f); glVertex3f(1.0f, 1.0f, 1.0f);
	glTexCoord2f(1.0f, 1.0f); glVertex3f(1.0f, 1.0f, -1.0f);
	// 底面
	glNormal3f(0.0f, -1.0f, 0.0f);					    // 法线朝下
	glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, -1.0f, -1.0f);
	glTexCoord2f(0.0f, 1.0f); glVertex3f(1.0f, -1.0f, -1.0f);
	glTexCoord2f(0.0f, 0.0f); glVertex3f(1.0f, -1.0f, 1.0f);
	glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f);
	// 右侧面
	glNormal3f(1.0f, 0.0f, 0.0f);					    // 法线朝右
	glTexCoord2f(1.0f, 0.0f); glVertex3f(1.0f, -1.0f, -1.0f);
	glTexCoord2f(1.0f, 1.0f); glVertex3f(1.0f, 1.0f, -1.0f);
	glTexCoord2f(0.0f, 1.0f); glVertex3f(1.0f, 1.0f, 1.0f);
	glTexCoord2f(0.0f, 0.0f); glVertex3f(1.0f, -1.0f, 1.0f);
	// 左侧面
	glNormal3f(-1.0f, 0.0f, 0.0f);					    // 法线朝左
	glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f);
	glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f);
	glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f);
	glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f);
	glEnd();								            // 四边形绘制结束
	//glPopMatrix();


	/*绘制一个三角形*/
	glPushMatrix();
	glTranslatef(-8, 0.0, -2.0);
	glBegin(GL_TRIANGLES);
	glVertex3f(0.0, 0.0, -1.0);
	glVertex3f(-1.0, 0.0, 1.0);
	glVertex3f(1.0, 0.0, 1.0);
	glEnd();

	glTranslatef(-5.0, 0.0, 0.0);
	//glEnable(GL_TEXTURE_2D);
	//glBindTexture(GL_TEXTURE_2D, g_groundtex);
	glutSolidSphere(3.0, 20, 20);
	//glDisable(GL_TEXTURE_2D);
	glPopMatrix();
	glPopAttrib();
}

//绘制球体的函数
void drawSphere()
{
	glEnable(GL_LIGHTING);
	glPushMatrix();
	glPushAttrib(GL_CURRENT_BIT);
	glColor3f(0.0, 0.5, 0.8);
	glTranslatef(g_yRot, g_xRot, 4);//物体位置
	glutSolidSphere(0.3, 20, 20);
	glPopMatrix();
	glPopAttrib();
	glDisable(GL_LIGHTING);
}


// 绘制场景
void RenderScene(void)
{



	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glLoadIdentity();
	glTranslatef(0.0, 0.0, 0.0);
	//// 移动z轴坐标系看到前面的一切东西
	glLightfv(GL_LIGHT0, GL_POSITION, lightPos);/*实时改变光源的位置*/
	//glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, spotDir);/*聚光灯效果*/
	//glLightf(GL_LIGHT0, GL_SPOT_CUTOFF, 60.0f);
	gluLookAt(g_cameraX, g_cameraY, g_cameraZ, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);


	//地面任意三点 
	M3DVector3f points[3] = { { -3.0f, -10.0f, -2.0f },
	{ -3.0f, -10.0f, 2.0f },
	{ 4.0f, -10.0f, 2.0f } };
	// 从三点得到的平面方程
	M3DVector4f vPlaneEquation;
	m3dGetPlaneEquation(vPlaneEquation, points[0], points[1], points[2]);

	// 计算投影在一个平面上的阴影矩阵
	m3dMakePlanarShadowMatrix(shadowMat, vPlaneEquation, lightPos);
	//glEnable(GL_NORMALIZE);


	glPushMatrix();
	//绘制平面即阴影体的依附平面
	glEnable(GL_TEXTURE_2D);
	glBindTexture(GL_TEXTURE_2D, g_groundtex);
	float lenghtGround = 80.0;//地面长宽度
	float heightGround = -11.0;
	glBegin(GL_QUADS);
	glTexCoord2f(0.0f, 0.0f); glVertex3f(-lenghtGround, heightGround, -lenghtGround);	// 纹理和四边形的左下
	glTexCoord2f(1.0f, 0.0f); glVertex3f(-lenghtGround, heightGround, lenghtGround);	// 纹理和四边形的右下
	glTexCoord2f(1.0f, 1.0f); glVertex3f(lenghtGround, heightGround, lenghtGround);	    // 纹理和四边形的右上
	glTexCoord2f(0.0f, 1.0f); glVertex3f(lenghtGround,heightGround, -lenghtGround);	// 纹理和四边形的左上
	glEnd();
	glDisable(GL_TEXTURE_2D);
	glPopMatrix();



	// 保存矩阵状态并进行旋转
	glPushMatrix();
	// 在绘制物体前将光源放在合适位置
	glEnable(GL_LIGHTING);
	glRotatef(g_xRot, 1.0f, 0.0f, 0.0f);
	glRotatef(g_yRot, 0.0f, 1.0f, 0.0f);
	DrawaScenes(0);/*绘制物体*/
	// 恢复原矩阵状态
	glPopMatrix();


	//// 绘制阴影与地面
	//// 首先关闭光照和深度测试
	glDisable(GL_DEPTH_TEST);
	glDisable(GL_LIGHTING);
	glPushMatrix();
	// 绘制光源体
	glPushMatrix();
	glTranslatef(lightPos[0], lightPos[1], lightPos[2]);
	glColor3f(1.0, 0.0, 0.0);
	glutSolidSphere(0.5f, 20, 20);
	glPopMatrix();

	glEnable(GL_DEPTH_TEST);
	glDisable(GL_STENCIL_TEST);
	drawSphere();
	glClearStencil(0x0);
	glClear(GL_STENCIL_BUFFER_BIT);
	glStencilFunc(GL_ALWAYS, 1, 0xFF);
	glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
	glEnable(GL_STENCIL_TEST);
	glDisable(GL_LIGHTING);

	/*绘制平面镜子*/
	glPushMatrix();
	glPushAttrib(GL_CURRENT_BIT);
	glColor3f(1.0f, 1.0f, 1.0f);
	glDepthMask(GL_FALSE);//设置深度缓冲区为只读
	glEnable(GL_TEXTURE_2D);
	glBindTexture(GL_TEXTURE_2D, g_mirrorTex);
	float lengthMirror = 10.0;//镜子长宽
	glBegin(GL_QUADS);
	//前面
	glTexCoord2f(0.0f, 0.0f); glVertex3f(-lengthMirror, -lengthMirror, -2.0f);	// 纹理和四边形的左下
	glTexCoord2f(1.0f, 0.0f); glVertex3f(lengthMirror, -lengthMirror, -2.0f);	// 纹理和四边形的右下
	glTexCoord2f(1.0f, 1.0f); glVertex3f(lengthMirror, lengthMirror, -2.0f);	    // 纹理和四边形的右上
	glTexCoord2f(0.0f, 1.0f); glVertex3f(-lengthMirror, lengthMirror, -2.0f);	// 纹理和四边形的左上
	glEnd();
	glDisable(GL_TEXTURE_2D);
	glPopMatrix();
	glPopAttrib();

	/*绘制光源体*/

	glPushMatrix();
	glTranslatef(lightPos[0], lightPos[1], lightPos[2]);
	glColor3f(1.0, 1.0, 1.0);
	glutSolidSphere(0.1f, 20, 20);
	glPopMatrix();

	//// 乘以阴影矩阵
	glMultMatrixf((float *)shadowMat);

	//// 旋转物体
	glRotatef(g_xRot, 1.0f, 0.0f, 0.0f);
	glRotatef(g_yRot, 0.0f, 1.0f, 0.0f);
	// 给予true来绘制阴影
	DrawaScenes(1);
	// 恢复矩阵
	glPopMatrix();

	

	glDepthMask(GL_TRUE);//重新启用深度缓冲区
	///明确说明函数的测试功能GL_NEVER,,GL_LESS, GL_LEQUAL,
	//GL_GREATER, GL_GEQUAL,,GL_EQUAL, GL_NOTEQUAL和 GL_ALWAYS。
	//初始化的值是GL_ALWAYS
	//ref
	//明确说明该模板测试的引用值。 ref 值被限制在0~2 ^ (n - 1)间,
	//其中n是模板缓存中位平面数。初始化值是0
	//mask
	//	该参数表示一个模板,用来和ref值以及存储的模板值做与运算。初始化值是全1
	//要允许或禁止该测试的话,使用glEnable(GL_STENCIL_TEST)或glDisable(GL_STENCIL_TEST)
	glStencilFunc(GL_EQUAL, 1, 0xFF);//0xff==255
	//glStencilOp函数,它用来根据比较结果修改蒙板缓存区中的值
	//sfail当蒙板测试失败时所执行的操作
	//zfail当蒙板测试通过,深度测试失败时所执行的操作
	//zpass当蒙板测试通过,深度测试通过时所执行的操作
	glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
	glScalef(1.0f, 1.0f, -1.0f);//镜子里的影子方向取反
	glPushAttrib(GL_CURRENT_BIT);
	drawSphere();
	glPopAttrib();
	glDisable(GL_STENCIL_TEST);
	glDepthMask(true);//重新启用深度缓冲区

	glutSwapBuffers();
}

void SetupRC()
{

	InitTexture();/*初始化图片纹理*/
	glShadeModel(GL_SMOOTH);
	glEnable(GL_DEPTH_TEST);	// 隐藏面消除;
	glFrontFace(GL_CCW);		// 逆时针多边形面
	glEnable(GL_CULL_FACE);     // 剔除内部表面影响
	// 启动光源0
	glLightfv(GL_LIGHT0, GL_AMBIENT, ambientLight);
	glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuseLight);
	glLightfv(GL_LIGHT0, GL_SPECULAR, specular);
	glLightfv(GL_LIGHT0, GL_POSITION, lightPos);
	glEnable(GL_LIGHT0);

	// 启用颜色跟踪
	glEnable(GL_COLOR_MATERIAL);
	// 实时设置材料属性遵循glColor值
	glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);

	// 所有材料有全反射
	glMaterialfv(GL_FRONT, GL_SPECULAR, specref);/*指定用于光照计算的当前材质属性*/
	glMateriali(GL_FRONT, GL_SHININESS, 128);/*高亮显示*/

	// 黑色背景
	glClearColor(0.0f, 0.0f, 0.0f, 1.0f);


}

void SpecialKeys(int key, int x, int y)
{
	if (key == GLUT_KEY_UP)
		g_xRot -= 1.0f;

	if (key == GLUT_KEY_DOWN)
		g_xRot += 1.0f;

	if (key == GLUT_KEY_LEFT)
		g_yRot -= 1.0f;

	if (key == GLUT_KEY_RIGHT)
		g_yRot += 1.0f;
	// Refresh the Window
	glutPostRedisplay();
}

void NormalKeys(unsigned char key, int x, int y)
{
	switch (key) {
	case VK_ESCAPE:exit(0); break;
		/*摄像机移动命令*/
	case 'w': g_cameraZ -= 1.0; glutPostRedisplay(); break;
	case 's': g_cameraZ += 1.0; glutPostRedisplay(); break;
	case 'a':lightPos[0] += 2.0; glutPostRedisplay(); break;
	case 'd':lightPos[0] -= 2.0; glutPostRedisplay(); break;
	default:break;

	}
}

void ChangeSize(int w, int h)
{
	float fAspect;

	// 防止被0除
	if (h == 0)
		h = 1;
	glViewport(0, 0, w, h);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	fAspect = (float)w / (float)h;
	gluPerspective(60.0f, fAspect, 0.1, 2000.0);
	glMatrixMode(GL_MODELVIEW);

}

int main(int argc, char* argv[])
{
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH | GLUT_STENCIL);
	glutInitWindowSize(1366, 768);
	glutCreateWindow("LightShadowMirrorReflect");
	glutReshapeFunc(ChangeSize);
	glutSpecialFunc(SpecialKeys);
	glutKeyboardFunc(NormalKeys);
	glutDisplayFunc(RenderScene);
	SetupRC();
	glutMainLoop();

	return 0;
}

光照阴影镜面反射综合算法: