Opengl入门版Windows教程-课程笔记

举报
鱼酱 发表于 2022/01/06 22:49:45 2022/01/06
【摘要】 文章目录 窗口创建笛卡尔坐标系点绘制线绘制CCW三角形绘制坐标系介绍矩阵变换矩阵压栈和弹栈BMP文件解析纹理绘制光照OBJ文件解析OBJ绘制SOIL库学习中... 窗口创建 Windows...

窗口创建

Windows API窗口创建

//窗口相关头文件
#include <Windows.h>
#include <gl/GL.h>
#include <gl/GLU.h>

//链接opengl库,windows平台自带
#pragma comment(lib,"opengl32.lib")
#pragma comment(lib,"glu32.lib")
//窗口回调函数
LRESULT CALLBACK GLWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	switch (msg)
	{
		//点击关闭按钮
	case WM_CLOSE:
		//给窗口发送 WM_QUIT消息
		PostQuitMessage(0);
		break;
	default:
		break;
	}
	//运行默认
	return DefWindowProc(hwnd, msg,wParam,lParam);
}

//windows主函数
INT WINAPI main(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd)
{	
	//1.注册窗口

	//窗体结构体
	WNDCLASSEX wndclass;
	//窗口额外空间
	wndclass.cbClsExtra = 0;
	//结构体大小
	wndclass.cbSize = sizeof(WNDCLASSEX);
	//不需要为窗口准备额外空间
	wndclass.cbWndExtra = 0;
	//背景
	wndclass.hbrBackground = NULL;
	//光标,整个箭头图标
	wndclass.hCursor = LoadCursor(NULL,IDC_ARROW);
	//应用程序图标
	wndclass.hIcon = NULL;
	//窗体图标
	wndclass.hIconSm = NULL;
	//窗口实例
	wndclass.hInstance = hInstance;
	//消息响应函数
	wndclass.lpfnWndProc = GLWindowProc;
	//窗口标题
	wndclass.lpszClassName = "GLWindow";
	//菜单
	wndclass.lpszMenuName = nullptr;
	//水平重绘和竖直重绘
	wndclass.style = CS_VREDRAW | CS_HREDRAW;
		
	//注册窗口
	ATOM atom = RegisterClassEx(&wndclass);
	if (!atom)
	{
		return 0 ;
	}

	//创建窗口,详情去看windowsAPI
	HWND hwnd = CreateWindowEx(NULL, "GLwindow", "Opengl  Window", WS_OVERLAPPEDWINDOW, 100, 100, 800, 600, nullptr, nullptr, hInstance, nullptr);
	HDC dc = GetDC(hwnd);

	//颜色描述符,选取opengl渲染像素格式
	PIXELFORMATDESCRIPTOR pfd;
	memset(&pfd, 0, sizeof(PIXELFORMATDESCRIPTOR));
	pfd.nVersion = 1;
	pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
	//颜色缓冲区
	pfd.cColorBits = 32;
	//深度缓冲区
	pfd.cDepthBits = 24;
	pfd.cStencilBits = 8;
	//RGBA颜色格式
	pfd.iPixelType = PFD_TYPE_RGBA;
	pfd.iLayerType = PFD_MAIN_PLANE;
		
	//窗口渲染、支持opengl、双缓冲
	pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
	
	//选择颜色描述
	int pixelFormat = ChoosePixelFormat(dc,&pfd);
	//设置颜色格式
	SetPixelFormat(dc, pixelFormat, &pfd);
	
	//创建上下文
	HGLRC rc = wglCreateContext(dc);
	//与HDC窗口关联
	wglMakeCurrent(dc, rc);

	//操作投影矩阵
	glMatrixMode(GL_PROJECTION);
	//角度  witdh/height 近端点  远端点
	gluPerspective(50.0f, 800.0f/600.0f,0.1f,1000.0f);

	//模型矩阵
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();

	//设置背景颜色
	glClearColor(0.1f,0.4f,0.6f,1.0f);


	//显示窗口
	ShowWindow(hwnd,SW_SHOW);
	UpdateWindow(hwnd);
	
	MSG msg;
	while (true)
	{	
		//获取到消息,并且从顶端移除
		if (PeekMessage(&msg, NULL, NULL, NULL,PM_REMOVE))
		{
			if (WM_QUIT == msg.message )
			{
				break;
			}

			//转换为应用程序消息
			TranslateMessage(&msg);
			//分发消息到回调函数
			DispatchMessage(&msg);	

            //draw to do			
			SwapBuffers(dc);
		}
	}


	return 0;
}
		

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137

笛卡尔坐标系

OpenGL是一个右手坐标系。简单来说,就是正x轴在你的右手边,正y轴朝上,而正z轴是朝向后方的。想象你的屏幕处于三个轴的中心,则正z轴穿过你的屏幕朝向你。坐标系画起来如下:
在这里插入图片描述

为了理解为什么被称为右手坐标系,按如下的步骤做:

  • 沿着正y轴方向伸出你的右臂,手指着上方。
  • 大拇指指向右方。
  • 食指指向上方。
  • 中指向下弯曲90度。

点绘制

glPointSize(20.0f); //点大小
glBegin(GL_POINTS);//开启点绘制
glVertex3f(0.0f, 0.0f, -0.5f);//坐标
glEnd();//end

  
 
  • 1
  • 2
  • 3
  • 4

线绘制

  • GL_LINES
    连线方式独立的线段 1-2 3-4 5-6
  • GL_LINE_STRIP
    连线方式1-2-3-4-5

glBegin(GL_LINE_STRIP); //开启线绘制
glColor4ub(255, 0, 0, 255);//设置颜色
glVertex3f(0.0f, 0.0f, -10.0f);//设置顶点坐标1
glColor4ub(0, 255, 0, 255);//设置颜色
glVertex3f(-5.0f, 0.0f, -10.0f);//设置顶点坐标2
glColor4ub(0, 0, 255, 255);//设置颜色
glVertex3f(-5.0f, -2.0f, -10.0f);//设置顶点坐标3
glEnd();//end

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • GL_LINE_LOOP
    连线方式1-2-3-4-5-1

CCW

opengl默认是,设置逆时针方向为正面
开启GL_CULL_FACE,会剔除背面,即顺时针

三角形绘制

  • GL_TRIANGLES //三角形
    连线方式1-2-3
  • GL_TRIANGLE_STRIP //三角形条带
    连线方式 1-2-3 3-2-4 5-4-6
  • GL_TRIANGLE_FAN //三角形扇
    连线方式1-2-3 1-3-4 1-4-5

坐标系介绍

屏幕标准化:标准化设备坐标(Normalized Device Coordinate, NDC)。也就是说,每个顶点的x,y,z坐标都应该在-1.0到1.0之间。
世界坐标系:物体在最终3D场景中的摆放位置对应的坐标所属的坐标系代表的空间.
局部空间:指物体所在的坐标空间
观察空间:观察空间是将世界空间坐标转化为用户视野前方的坐标而产生的结果。
裁剪空间:因为将所有可见的坐标都指定在-1.0到1.0的范围内不是很直观,所以我们会指定自己的坐标集(Coordinate Set)并将它变换回标准化设备坐标系,就像OpenGL期望的那样。
被裁剪掉的坐标就会被忽略,所以剩下的坐标就将变为屏幕上可见的片段。
观察空间:从摄像机的视角所观察到的空间而这通常是由一系列的位移和旋转的组合来完成,平移/旋转场景从而使得特定的对象被变换到摄像机的前方。这些组合在一起的变换通常存储在一个观察矩阵(View Matrix)里。
平截头体:由投影矩阵创建的观察箱(Viewing Box)。

矩阵变换

MVP矩阵介绍

  • Model 模型矩阵:模型变换通过乘以模型矩阵来实现。模型矩阵包含这平移,旋转,缩放的信息。
  • View 观察矩阵:把所有的世界坐标变换为相对于摄像机位置与方向的观察坐标。
  • Project 投影矩阵:为了将顶点坐标从观察变换到裁剪空间,我们需要定义一个投影矩阵(Projection Matrix)。

固定管线里只要知道,下面的API

//沿着x\y\z轴放大缩小
glScalef( x, y, z);
//沿着x\y\z轴 移动
glTranslatef(x, y, z)
//沿着x、y、z轴旋转
glRotatef(angle, x, y, z) 

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

这三个API其实内部都是矩阵操作,矩阵不具有交换律,
所以移动、旋转、平移和旋转、移动、平移得到的结果是不一样的。

矩阵变化学习

https://learnopengl-cn.github.io/01%20Getting%20started/07%20Transformations/#_16

矩阵压栈和弹栈

glPushMatrix、glPopMatrix操作事实上就相当于栈里的入栈和出栈。
栈结构特点:先进的后出,后进先出。
eg:

glLoadIdentity();
glTranslatef(1,0,0);//向右移动(1,0,0)
glPushMatrix();//保存当前位置
glTranslatef(0,1,0);//如今是(1,1,0)了
glPopMatrix();//这样,如今又回到(1,0,0)了

  
 
  • 1
  • 2
  • 3
  • 4
  • 5

BMP文件解析

//
unsigned char* LoadFileContent(const char* filePath)
{
	unsigned char* fileContent = nullptr;
	FILE* pFile = fopen(filePath, "rb");
	if (pFile)
	{
		//read
		fseek(pFile, 0, SEEK_END);
		int nLen = ftell(pFile);
		if (nLen > 0)
		{
			rewind(pFile);
			fileContent = new unsigned char[nLen + 1];
			fread(fileContent, sizeof(unsigned char), nLen, pFile);
			fileContent[nLen] = '\0';
		}
		fclose(pFile);
	}
	return fileContent;
}


//返回rgb调色板
unsigned char* DecodeBMP(unsigned char* bmpFileData, int& width, int& height)
{
	if (0x4D42 == *((unsigned short*)bmpFileData)) //BMP图片开头固定格式
	{
		int pixelDataOffset = *((int*)(bmpFileData + 10));//调色板便宜
		width = *((int*)(bmpFileData + 18));	//宽度
		height = *((int*)(bmpFileData + 22));//高度
		unsigned char* pixelData = bmpFileData + pixelDataOffset; //数据开始指针
		//bgr bgr bgr ....
		//bgr->rgb
		//rgb rgb rgb
		for (int i = 0; i < width * height * 3; i += 3)
		{
			unsigned char temp = pixelData[i];
			pixelData[i] = pixelData[i + 2];
			pixelData[i + 2] = temp;
		}
		return pixelData;
	}
	else
	{
		return nullptr;
	}
}


  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49

纹理绘制

glGenTextures(1, &mTextureID);//生成一个纹理
glBindTexture(GL_TEXTURE_2D, mTextureID);//绑定目标纹理
//operation on current texture 纹理的参数设置
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); //线性过滤
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, pixelData);
glBindTexture(GL_TEXTURE_2D, 0);

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
//开启2D纹理
glEnable(GL_TEXTURE_2D);

  
 
  • 1
  • 2

光照

  • 法线:法向量是一个垂直于顶点表面的(单位)向量。由于顶点本身并没有表面(它只是空间中一个独立的点),我们利用它周围的顶点来计算出这个顶点的表面。
  • 环境光 Ambient :周围分散的很多光源,就是全局光源。
  • 漫反射光:把光线向所有方向均匀的散射.(面向光源的表面更亮。)
  • 镜面反射光:在入射光和观察者的视角都在某个特定的角度时会使物体高度发光。(模拟光在抛光或金属表面的反射,有光斑)
//光照颜色
float blackColor[] = { 0.0f,0.0f,0.0f,1.0f };
float whiteColor[] = { 1.0f,1.0f,1.0f,1.0f };
float lightPos[] = { 0.0f,1.0f,0.0f,0.0f };//
glLightfv(GL_LIGHT0, GL_AMBIENT, whiteColor);//环境光是由光源发出经环境多次散射而无法确定其入射方向的光,即似乎来自所有方向。其特征是入射方向和出射方向均为任意方向。 
glLightfv(GL_LIGHT0, GL_DIFFUSE, whiteColor);//漫射光来自特定方向,它垂直于物体时比倾斜时更明亮。一旦它照射到物体上,则在各个方向上均匀地发散出去,效果为无论视点在哪里它都一样亮,其特征是入射方向唯一、出射方向为任意方向。
glLightfv(GL_LIGHT0, GL_SPECULAR, whiteColor);//镜面光 来自特定方向并沿另一方向反射出去
glLightfv(GL_LIGHT0, GL_POSITION, lightPos);//direction light,point,spot

float blackMat[] = { 0.0f,0.0f,0.0f,1.0f };
float ambientMat[] = { 0.1f,0.1f,0.1f,1.0f };
float diffuseMat[] = { 0.4f,0.4f,0.4f,1.0f };
float specularMat[] = { 0.9f,0.9f,0.9f,1.0f };

//光照方向、光照类型、反射率
glMaterialfv(GL_FRONT, GL_AMBIENT, ambientMat); 
glMaterialfv(GL_FRONT, GL_DIFFUSE, diffuseMat);
glMaterialfv(GL_FRONT, GL_SPECULAR, specularMat);



glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

OBJ文件解析

todo

OBJ绘制

SOIL库

Diy Cmake文件

cmake_minimum_required (VERSION 3.8)

aux_source_directory(. DIR_SRCS)


message("-------------------------------")
option(DLL_FLAG "is SHARED?" ON)
message(STATUS "SHARED switch: "${DLL_FLAG})
message("-------------------------------")

if(${DLL_FLAG} STREQUAL  "ON")
ADD_LIBRARY(soil SHARED  ${DIR_SRCS})
else()
ADD_LIBRARY(soil STATIC  ${DIR_SRCS})
endif(${DLL_FLAG} STREQUAL  "ON")


  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

todo

学习中…

文章来源: yujiang.blog.csdn.net,作者:鱼酱2333,版权归原作者所有,如需转载,请联系作者。

原文链接:yujiang.blog.csdn.net/article/details/114258861

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。