计算机图形学の三维几何变换&材质光照(OpenGL)

本文解析了OpenGL中的三维几何变换和材质光照,通过实例展示了如何创建一个简单的太阳系模型,包括太阳、地球和月球的光照效果与动态旋转。介绍了视点透视、光照设置、材质定义以及关键的旋转、平移和光照操作。
摘要由CSDN通过智能技术生成

OpenGL三维几何变换 & 材质光照

显示效果:

太阳系

源码:

#define NDEBUG
#ifndef GLUT_DISABLE_ATEXIT_HACK
#define GLUT_DISABLE_ATEXIT_HACK
#endif
#include <windows.h>
#include <gl/glut.h>
#include <math.h>
#include <stdio.h>
#include <vector>

using namespace std;

const int windowWidge=600, windowHeight=600;

static GLfloat angle = 0.0f;
static GLfloat moonAngle=0.0f;
static GLfloat sunAngle=0.0f;


//初始化绘制
void init(){
	glClearColor(0.0,0.0,0.0,0);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);  //清理颜色和深度缓存

	// 创建透视效果视图
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(80.0, 1.0f, 1.0f, 50.0f);

	//定义视点位置:
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	gluLookAt(0.0, 30.0, 10.0, 0.0, 0.0, 0.0, 0.0, -1, 3.0);

	// 定义太阳光源,它是一种白色的光源
	{
		GLfloat sun_light_position[] = {0.0f, 0.0f, 0.0f, 1.0f}; //光源的位置在世界坐标系圆心,齐次坐标形式
		GLfloat sun_light_ambient[]   = {0.1f, 0.1f, 0.1f, 1.0f}; //RGBA模式的环境光
		GLfloat sun_light_diffuse[]   = {1.0f, 234.0/255, 106.0/255, 1.0f}; //RGBA模式的漫反射光,全白光
		GLfloat sun_light_specular[] = {1.0f, 234.0/255, 106.0/255, 1.0f};  //RGBA模式下的镜面光 ,全白光
		glLightfv(GL_LIGHT0, GL_POSITION, sun_light_position);
		glLightfv(GL_LIGHT0, GL_AMBIENT,   sun_light_ambient);
		glLightfv(GL_LIGHT0, GL_DIFFUSE,   sun_light_diffuse);
		glLightfv(GL_LIGHT0, GL_SPECULAR, sun_light_specular);

		//开启灯光
		glEnable(GL_LIGHT0);
		glEnable(GL_LIGHTING);
		glEnable(GL_DEPTH_TEST);
	}
	return ;
}


void display(){
	glClearColor(0.0,0.0,0.0,0);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	// 定义太阳的材质并绘制太阳
	{
		GLfloat sun_mat_ambient[]   = {1.0f, 234.0/255, 106.0/255, 1.0f};  //定义材质的环境光颜色
		GLfloat sun_mat_diffuse[]   = {0.0f, 0.0f, 0.0f, 1.0f};  //定义材质的漫反射光颜色
		GLfloat sun_mat_specular[] = {0.0f, 0.0f, 0.0f, 1.0f};   //定义材质的镜面反射光颜色
		GLfloat sun_mat_emission[] = {1.0f, 234.0/255, 106.0/255, 1.0f};   //定义材质的辐射光颜色
		GLfloat sun_mat_shininess   = 0.0f;
		glMaterialfv(GL_FRONT, GL_AMBIENT,    sun_mat_ambient);
		glMaterialfv(GL_FRONT, GL_DIFFUSE,    sun_mat_diffuse);
		glMaterialfv(GL_FRONT, GL_SPECULAR,   sun_mat_specular);
		glMaterialfv(GL_FRONT, GL_EMISSION,   sun_mat_emission);
		glMaterialf (GL_FRONT, GL_SHININESS, sun_mat_shininess);
		glPushMatrix ();
		glRotated (sunAngle,0.0f, 0.0f, 1.0f);
		glutSolidSphere(4.0, 40, 32);
		glPopMatrix ();
	}

	// 定义地球的材质并绘制地球
	{
		GLfloat earth_mat_ambient[]   = {0.0f, 0.0f, 1.0f, 1.0f};  //定义材质的环境光颜色
		GLfloat earth_mat_diffuse[]   = {0.0f, 0.0f, 1.0f, 1.0f};  //定义材质的漫反射光颜色
		GLfloat earth_mat_specular[] = {1.0f, 234.0/255, 106.0/255, 1.0f};   //定义材质的镜面反射光颜色
		GLfloat earth_mat_emission[] = {0.0f, 0.0f, 0.0f, 1.0f};   //定义材质的辐射光颜色
		GLfloat earth_mat_shininess   = 32.0f;
		glMaterialfv(GL_FRONT, GL_AMBIENT,    earth_mat_ambient);
		glMaterialfv(GL_FRONT, GL_DIFFUSE,    earth_mat_diffuse);
		glMaterialfv(GL_FRONT, GL_SPECULAR,   earth_mat_specular);
		glMaterialfv(GL_FRONT, GL_EMISSION,   earth_mat_emission);
		glMaterialf (GL_FRONT, GL_SHININESS, earth_mat_shininess);
		glPushMatrix ();
		glRotatef(angle, 0.0f, 0.0f, 1.0f);
		glTranslatef(10.0f, 0.0f, 0.0f);
		glutSolidSphere(2.0, 40, 32);		//半径为2, 40条纬线, 32条经线
		glPopMatrix ();
	}
//	定义月球材质并绘制月球
	{
		GLfloat moon_mat_ambient[]   = {225.0/255, 225.0/255, 220.0/255, 1.0f};  //定义材质的环境光颜色
		GLfloat moon_mat_diffuse[]   = {225.0/255, 225.0/255, 220.0/255, 1.0f};  //定义材质的漫反射光颜色
		GLfloat moon_mat_specular[] = {1.0f, 234.0/255, 106.0/255, 1.0f};   //定义材质的镜面反射光颜色
		GLfloat moon_mat_emission[] = {0.0f, 0.0f, 0.0f, 1.0f};   //定义材质的辐射光颜色
		GLfloat moon_mat_shininess   = 128.0f;
		glMaterialfv(GL_FRONT, GL_AMBIENT,    moon_mat_ambient);
		glMaterialfv(GL_FRONT, GL_DIFFUSE,    moon_mat_diffuse);
		glMaterialfv(GL_FRONT, GL_SPECULAR,   moon_mat_specular);
		glMaterialfv(GL_FRONT, GL_EMISSION,   moon_mat_emission);
		glMaterialf (GL_FRONT, GL_SHININESS, moon_mat_shininess);

		//绕地球旋转:
		glPushMatrix ();
		glRotatef(angle, 0.0f, 0.0f, 1.0f);
		glTranslatef(10.0f, 0.0f, 0.0f);
		glRotatef(-angle, 0.0f, 0.0f, 1.0f);
		glRotatef (moonAngle,0.0f, 0.0f, 1.0f);
		glTranslatef (4.0f, 0.0f, 0.0f);
		glutSolidSphere(1.0, 40, 32);		//半径为2, 40条纬线, 32条经线
		glPopMatrix ();
	}
	glutSwapBuffers();

	return ;
}

void myIdle(void){
	angle += 0.5f;
	moonAngle +=2.0f;
	sunAngle +=1.0f;
	if( angle >= 360.0f ){
		angle = 0.0f;
	}
	if(moonAngle >=360.0f){
		moonAngle=0.0f;
	}
	glutPostRedisplay();
    Sleep(10);
    return;
}


int main(int argc, char** argv){
	glutInit(&argc, argv);//初始化glut
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);//设置显示模式为单缓冲,RGB模式
	glutInitWindowPosition(0,0);//设置窗口位置
	glutInitWindowSize(windowWidge,windowHeight);//设置窗口大小
	glutCreateWindow("太阳系");//设置窗口标题
	init();
	glutDisplayFunc(display);
	glutIdleFunc(myIdle);
	glutMainLoop();
	return 0;
}

源码分析:

视点透视:

// 创建透视效果视图
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(80.0, 1.0f, 1.0f, 50.0f);
  • 先搞明白glMatrixMode()

    glMatrixMode是用来指定哪一个矩阵是当前矩阵,而它的参数代表要操作的目标

    GL_PROJECTION是对投影矩阵操作
    GL_MODELVIEW是对模型视景矩阵操作
    GL_TEXTURE是对纹理矩阵进行随后的操作

  • 之前讲到openGL有两种投影: 正射投影(垂直投影)和透视投影

    image-20201023163616445

    前者不用缩放, 后者使用缩放

  • 通常,我们需要在进行变换前把当前矩阵设置为单位矩阵。
    所以后头会跟上一个:

    glLoadIdentity();

  • gluPerspective(80.0, 1.0f, 1.0f, 50.0f);

    设置当前可视空间为透视投影空间, 并设置透视投影的参数

    之前用的都是glOrtho
    此为设置当前可视空间为正投影空间

    void gluPerspective (GLdouble fovy, GLdouble aspect, GLdouble zNear, GLdouble zFar)
    

    几个参数与图中的视角相对应:

    1. 视线张角

      当张角变小时, 相当于显微镜, 将更小范围内的东西放大到固定的屏幕, 反之缩小

      当=180时, 物体变的无限小, 不可见, 当=0时, 视线关闭, 不可见

    2. 宽高比w/h

    3. zNear表示近裁剪面到眼睛的距离,zFar表示远裁剪面到眼睛的距离

      这两个都不能设置为负值

定义视点:

//定义视点:
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(0.0, 30.0, 10.0, 0.0, 0.0, 0.0, 0.0, -1, 3.0);
  • 前两个和之前一样

  •   void gluLookAt(GLdouble eyex,GLdouble eyey,GLdouble eyez,
                     GLdouble centerx,GLdouble centery,GLdouble centerz,
                     GLdouble upx,GLdouble upy,GLdouble upz);
    

    定义视点, 分为三组坐标

    1. 第一组定义了视点在世界坐标系中的坐标
    2. 第二组定义了视点看向的位置
    3. 第三组定义了视点向上方向的在世界坐标系中的方向向量(头顶位置)

光照:

具体的光照详解可以参考这一篇:

https://blog.csdn.net/chuifuhuo6864/article/details/100886020

三维变换:

三维变换具体可以参考这篇博客:

https://www.cnblogs.com/tjulym/p/5049313.html

glPushMatrix ();
glRotatef(angle, 0.0f, 0.0f, 1.0f);
glTranslatef(10.0f, 0.0f, 0.0f);
glutSolidSphere(2.0, 40, 32);		//半径为2, 40条纬线, 32条经线
glPopMatrix ();
  • 矩阵入栈, 而后对此矩阵进行操作

以下的操作都可以看做是对当前坐标轴的变换, 初始坐标轴与世界坐标轴重合

  • 旋转:

    glRotatef(GLfloat angle,GLfloat x,GLfloat y,GLfloat z)
    
    1. angle:
      旋转的角度

    2. 后头一组坐标为旋转轴的方向向量

      将以此向量为上进行逆时针旋转

  • 平移:

    void glTranslatef(GLfloat x,GLfloat y,GLfloat z);
    

    以当前坐标轴为基准, 将当前坐标轴平移到(x,y,z)的位置

  • 画一个刚体球:

    void glutSolidSphere(GLdouble r,GLint ,GLint); 
    
    • r
      球的半径
    • 后头两个参数为球的纬线&经线的条数
      实际画出的是一个多面体, 所以纬线与经线越多, 就越接近真实球体
  • 弹出当前操作的矩阵

    矩阵堆栈有容量上限, 及时弹出防止爆栈

画面刷新:

glutIdleFunc(myIdle);
void myIdle(void){
	angle += 0.5f;
	moonAngle +=2.0f;
	sunAngle +=1.0f;
	if( angle >= 360.0f ){
		angle = 0.0f;
	}
	if(moonAngle >=360.0f){
		moonAngle=0.0f;
	}
    sleep(10);
	glutPostRedisplay();
    return;
}
  • glutIdleFunc(void (*callback) ())

    闲时调用函数, 当openGL空闲时会调用传给他的函数func

  • myIdle()

    返回值与参数均为void

    在空闲时被调用, 更新当前角度啥的

    而后一个sleep() 防止刷新率过高

  • glutPostRedisplay()

    相当于直接调用display()来刷新窗口, 但后者更加智能一点

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值