一、准备
最近,闲来无事,研究了一下SDL2,发现SDL2作为一个开放的平台,确实比较简单,而且在多媒体方面,图像操作方面,跨平台方面确实有优势,实在不行了还有源码可以参考,但也有其不方便的一面,如没有文字显示功能,一个简单的抠图,就难倒一片人,2D作图还行,3D作图就更困难了。OpenGL作为同样跨平台的工具,在2D,3D方面有其独到之处,特别是在可编程管线方面,更加灵活高效。当然,还有一个选择就是QT,但QT也有其不足之处,尤其是发布时体量过大,如不搞大型工程,没有必要。另外,QT自成体系,学习起来需要付出一定的代价。
SDL2目前已经发布到2.20稳定版了,我的感觉,有2.07版就够用的 了。当然在新的版本开发也不错。
OpenGL就更加复杂了,网上的资料有的很陈旧,OpenGL本身也没有像SDL一样的持续发布的系统,还有,OpenGL自身就是一个状态机,在多线程方面有其天生的缺陷,不过与SDL2结合起来没有问题,因为SDL2自身,在显示方面,多线程就是一个弱项。
SDL2 与OpenGL结合,OpenGL版本有2.1就够用了,版本过高,在跨平台时可能会有问题。当然也可以选用更高的版本,但从所需要的功能来看,2.1已经够用了,如不设置,SDL2缺省是使用OpenGL 2.1,目前,win10所支持的版本为4.2,在win10上可能选用更高的版本,但从解决需求的角度来看,没有必要。版本高了没法向下兼容。
SDL2 直接从网上下载安装就行了,这里我就不再多重复了,这里重点说OpenGL版本,在SDL2 上OpenGL可以直接用,有例子可以直接参考,但受到很大局限,只能支持固定管线,而OpenGL用到的是GLSL 可编程管线,只能求助第三方工具,网上的资料多数用到GLUT,这是一个老掉牙的东西,2000年之后就没有过更新,而且,只支持32位,我一看就连碰都不碰,推荐可选的有GLAD和GLEW,GLAD的优势是不需要额外的库,使用简单,GLEW的优势是功能齐全,一个库包含了OPENGL所有的东西。可根据情况选用。至于资源,网上都有,到官网上下就可以了,就不重复了。
GLAD和GLEW都不需要安装,直接将相关文件解压后拷贝到相关目录中就可以了。
这是Iib目录文件分 x86和 x64
Sdl2dll
Sdl2.lib
Sdl2main.lib
Glew32.lib
Glew32s.lib
在以上工作完成之后 ,就可以开始编程了
我用的是VS2019
先是建立一个空的C++控制台项目?我是建立一个名为glsdltest的项目,在D盘根目录 下,
在vs2019主页面上点击文件->新建,选择建立空的C++控制台项目,选择项目名和所
在目录,完成即可,
要使用SDL和OpenGL在完成新建空项目后进行以下几步工作
1.是添加包含目录 打开项目->属性,打开VC++项,所有平台
在包含目录上加上 SDL2和附件目录 我的电脑是这样:
?d:\sdl2\include;$(IncludePath)
2.添加库目录 32位和64位平台分别加
32位X86下,在库目录上添加库的目录,我的电脑是这样
d:\sdl2\lib\x86;$(LibraryPath)
如使用64位编程,在x64下添加库目录 我的电脑是这样 :
d:\sdl2\lib\x64;$(LibraryPath)
3.添加链接资源,点开链接器? 在输入->附加依赖项加加上相关资源 我的电脑是这样
winmm.lib;opengl32.lib;sdl2.lib;sdl2main.lib;glew32s.lib;%(AdditionalDependencies);
其中:只用GLAD的,可以不加glew32s.lib;
4.建议,将VS 默认字符集改为UTF8 字符集。
到这里,编程环境就基本上配置好了。
现在可以按F5编译运行了,因为没有输入任何新的语句,程序跳出控制台,打印Hello World!
然后提示结束。
5.如使用GLAD请将glad.c 拷贝到项目目录下,并使用项目->添加现有项,加入项目中。
准备工作至此结束。
#include <iostream>
int main()
{
std::cout << "Hello World!\n";
}
二、第一个窗口
在SDL2上建立一个窗口是很简单的,由于是C++程序,我们需要新建一个类,点击项目->添加类
CMain 将main函数改成以下这个样子
main.app
#include "cmain.h"
#include <SDL.h>
int main(int argc, char* argv[])
{
SDL_Init((Uint32)(SDL_INIT_VIDEO));
CMain a;
return 0;
}
这时按下F5试一下,在X86下正常,但x64下报错,0xc000007b 这是DLL文件位数错,将64位的SDL.DLL拷贝到生成目录下就可以了,32位运行环境也需要将相应的SDL.DLL拷贝到相应生成目录中去,注意运行、调试,32位,64位共有4个目录,每个目录都需要拷贝。如只将SDL.DLL拷贝到运行目录中,一但更换编译位数,就会报错。好,继续往下进行。
打开一个窗口很简单,在cmain.app 和cmain.h 输入如下
cmain.h
#pragma once
#include <SDL.h>
//系统基本显示画面尺度
#define PictureWidth 640
#define PictureHeight 400
#define RealWidth 640
#define RealHeight 400
#define WindowWidth 800
#define WindowHeight 500
class CMain
{
public:
SDL_Window* gpWindow;
SDL_Renderer* gpSdlRender;
CMain();
};
cmain.app
#include "cmain.h"
CMain::CMain():
gpWindow(nullptr),
gpSdlRender(nullptr)
{
//建立窗口 不显示
gpWindow = SDL_CreateWindow("SDL2 + Opengl 演示 ......",
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, WindowWidth, WindowHeight,
SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_HIDDEN);
//在此加载其他内容
//显示窗口
SDL_ShowWindow(gpWindow);
//延时5秒
SDL_Delay(5000);
}
这时按F5还会报错,没有找到入口,,这时,需要定义程序的入口点,按下项目->属性->链接器->系统->子系统,将入口修改为WinDow窗口,这时按F5一个窗口出现在我们面前,延时5秒后自动退出。
三、加入OpenGL
?以上操作还不涉及OpenGL,在头文件 上输入以下代码:
#define GLAD
//#define GLEW
#ifdef GLEW
#define GLEW_STATIC
#endif
//如不显示文字,可注释掉以下语句
#include <Windows.h>
#ifdef GLAD
#include <glad/glad.h>
#else
#ifdef GLEW
#include <GL/glew.h>
#else
#include <GL/GL.h>
#include <GL/GLU.h>
#define GL_GLEXT_PROTOTYPES
#include <SDL_opengl.h>
#include <SDL_opengl_glext.h>
#endif
#endif
这样可以方便的在GLAD和GLEW之间切换,
另一部分代码在窗口和渲染器建立之后载入,载入的代码和顺序如下:
glPreInit();
gpWindow = SDL_CreateWindow("SDL2 + Opengl 演示 ......",
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, WindowWidth, WindowHeight,
SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_HIDDEN);
SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengl");
#if 0
//置opengl 上下文,
gpgl_Context = SDL_GL_CreateContext(gpWindow);
if (!gpgl_Context)
exit(2);
#else
gpSdlRender = SDL_CreateRenderer(gpWindow, -1, SDL_RENDERER_ACCELERATED);
//检查是否支持OPENGL,如不支持退出,退出码 8
SDL_RendererInfo rendererInfo;
SDL_GetRendererInfo(gpSdlRender, &rendererInfo);
if (strncmp(rendererInfo.name, "opengl", 6)) exit(8);
#endif
#ifdef GLAD
//装入glad函数指针
if (!gladLoadGLLoader(SDL_GL_GetProcAddress))
exit(2);
#else
#ifdef GLEW
if (glewInit())
exit(2);
#endif
#endif
这里要说明的是,载入OpenGL上下文有两种方法,可以用SDL2自带的SDL_CreateRenderer,如果该渲染器支持OpenGL,也可以用SDL2建立OpenGL上下文命令SDL_GL_CreateContext。if(0)改成if(1)都可以,反正我的运行环境都支持。如果不先输入? ?
SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengl");
这条命令,SDL2建立的渲染器可能不支持OPENGL上下文。
还有,glPreInit();要在窗口建立之前:内容见最后的例子:
注意!如想在OpenGL的着色器中使用SDL纹理,必须使用SDL 的渲染器隐式提供的OpenGL上下文,而不能使用OpenGL上下文,。
至此,已经可以运行OpenGL的各项命令了。
四、OpenGL坐标体系
OpenGL坐标体系非常复杂,有2D坐标系(x,y),3D坐标系,(x,y,z),正投影。透视投影,裁剪坐标系,视觉坐标系,世界坐标系,物体坐标系,彻底搞清楚这些坐标的区别和变换方法,需要很长的时间,好在目前需要的是与SDL2相适应的坐标方式,既最简单的2D变换方式,所谓2D变换方式,主要涉及点与点之间,面与面之间,矩形与矩形之间的简单变换。如在window图形编程中经常用到的? ? BitBlt 函数,可以实现不同矩形之间图像的拷贝和变换。而OpenGL作为图像处理专用工具,在2D作图时,不仅可以在不同的图像之间拷贝、转换和融合,还可以对图像轻而易举的进行拉伸和拓扑变形,对图像进行各种魔幻般的变换。为了实现这些功能,OpenGL最小2D坐标单元,是一个又一个的三角形,各种矩形和其他复杂的形状,都是由一个个的三角形拼接而成的。这些三角形的顶点,构成OpenGL的顶点坐标系。无论多复杂的图形,多复杂的物体,落实到计算机屏幕,都要统一成一个平面,因此2D坐标系是OpenGL的最终表现形式,而三角顶点坐标构成OpenGL的基本顶点坐标系。
有了三角顶点坐标,可以拼接几乎任意的图形,最简单的是矩形拼接,由两个三角形组成,我们定义矩形的四个顶点,每个顶点由两个数字(x,y)组成,共有8个数字,但如果是正四边形,只要用四个数字就可以描述了。SDL中有一个描述矩形的结构 SDL_Rect 分别是左上角的坐标和矩形的长度和宽度,而所有计算机的图片,都是正四边形的,这些图片的任意矩形边界,都可以用该结构描述。事实是OpenGL对图片(纹理)的描述,用的(u,v)坐标系,正好能从上述结构转换而成。
OpenGL对坐标进行归一化处理,正常情况下,顶点坐标范围在-1到1之间,纹理坐标在0到1之间。而且,颜色深度等计量范围大都在绝对值1的范围内,这与其他图形处理系统,有很大的区别。这样做的优点是很明显的,一是不同大小的纹理,可以统一处理,省去了不少计算量,二是,对不同大小的渲染目标,也可以统一处理。
顶点坐标载入有多种方法,一是逐个三角形输入,一个矩形有两个三角形组成,需要载入6个顶点,如矩形{a左下,b左上,c右上,d右下},第一个三角形{a,b,c},第二个三角形{a,c,d},共需要6个点的坐标,二是连续三角形输入,三是三角扇形多边形输入,对于矩形,只需要输入4个点,{a,b,c,d},这是在sdl+OPengl中用得最多的。
对于纹理坐标就比较简单了,按矩形{a,b,c,d}逐点输入就可以了。之后的例子中将介绍到。
五、GLSL 着色器
对于SDL2与OpenGL结合应用来讲,GLSL着色器及其相关操作非常关键,也是整个应用程序显示效率提高的关键,与SDL2纹理一样,GLSL着色器及其相关操作是针对GPU的,OpenGL实际起到扩展和补充SDL纹理的作用,而且因其可编程的特点,使用起来更加灵活便利。在这里我不想介绍其语法之类的东西,只介绍如何操作,如何具体使用。
GLSL? 着色器有两种编制形式,一是文件,一是字符串,如何使用全凭个人喜好。以下就是顶点着色器字符串的简单形式
const static GLchar* const vertexShader =
"#version 130\n\
in vec2 position;\n\
in vec2 TexCoord;\n\
out vec2 sTexCoord;\n\
void main()\n\
{\
gl_Position = vec4(position, 0.0, 1.0) ;\n\
sTexCoord = TexCoord ;\
}";
注意:中间行每行用 “\”结束,后面不能有可见和不可见字符,字符串最后面记得加分号,否则会报错。注释行必须以“\n\”结束,否则,就认为下一行接着注释。
?片元着色器:
const static GLchar* const fragmentShader =
"#version 130\n\
uniform sampler2D v_tex;\n\
in vec2 sTexCoord;\n\
out vec4 outColor;\n\
void main()\n\
{\
outColor = texture(v_tex,sTexCoord);\
}" ;
做过GLSL的会发现,以上着色器中间只载入了一个2D纹理,而没有载入顶点颜色,这是因为在针对SDL2的应用中,还未发现顶点颜色的用途。以上着色器的实际作用是将纹理渲染出来。
着色器的载入和编译,
inline GLuint loadShader(GLenum shaderType, const GLchar* source)
{
GLuint shaderId = glCreateShader(shaderType);
glShaderSource(shaderId, 1, &source, NULL);
glCompileShader(shaderId);
GLint result = GL_FALSE;
glGetShaderiv(shaderId, GL_COMPILE_STATUS, &result);
return shaderId;
}
inline GLuint compileProgram(GLuint fragmentShaderId, GLuint vertexShaderId)
{
GLuint programId = glCreateProgram();
glAttachShader(programId, vertexShaderId);
glAttachShader(programId, fragmentShaderId);
glLinkProgram(programId);
return programId;
}
//编译着色器,返回应该是大于0的正数
v_verticesID = loadShader(GL_VERTEX_SHADER, vertexShader);
v_fragmentID = loadShader(GL_FRAGMENT_SHADER, fragmentShader);
v_programID = compileProgram(v_verticesID, v_fragmentID);
六、OpenGL纹理和SDL纹理
由于是状态机的关系,OpenGL纹理在系统内部调用只是用 了一个正整数,而不是像其他系统要用固定的结构和指针来描述,管理纹理并没有统一的结构,每个开发者管理纹理所使用的结构不一定相同,这给开发者以更大的自由度的同时,也带来了更大的难度。所幸的是,SDL纹理在设计时,设计成可以作为GLSL纹理使用,这大大地方便了开发者,这比单独用OpenGL开发简单的多了,这也是sdl与OpenGL的GLSL结合开发的优势所在。
sdl纹理建立有多种方式,可以直接新建空纹理,也可以从SDL表面上建立
例如根据BMP文件建立纹理,只需要3步
//以下载入图片生成纹理
SDL_Surface* tst1 = SDL_LoadBMP("tst1.bmp");
SDL_Texture* txt1 = SDL_CreateTextureFromSurface(gpSdlRender, tst1);
SDL_FreeSurface(tst1);
第一步将图片生成SDL表面,第二步根据渲染器和表面生成纹理,第三步删除表面。
由此可以提出,使用SDL纹理是相当简单的事情,甚至不用关心纹理的内部结构。
对纹理的使用也相当简单,
glEnable(GL_TEXTURE_2D);
//绑定第一个纹理
if(rpText1)
{
glActiveTexture(GL_TEXTURE0);
SDL_GL_BindTexture(rpText1, 0, 0);
}
//绑定第二个纹理
if(rpText2)
{
glActiveTexture(GL_TEXTURE1);
SDL_GL_BindTexture(rpText2, 0, 0);
}
使用完成之后,释放资源
glActiveTexture(GL_TEXTURE0);
if(rpText1) SDL_GL_UnbindTexture(rpText1);
glActiveTexture(GL_TEXTURE1);
if (rpText2) SDL_GL_UnbindTexture(rpText2);
渲染到纹理,是OpenGL结合进SDL2的一个重要功能,渲染到纹理,能极大的提高渲染效果,简化GLSL的设计,将一些复杂的渲染过程,简化到分多个简单步骤来实现。
我们要渲染的对象叫帧缓存。也叫FBO。帧缓存是一个包含纹理和深度缓存(可选择)区的容器。?由于是2D应用,作为渲染的纹理,可以使用32位的RGBA,如果是3D 应用,还应该加上一个深度纹理。
所幸的是,sdl2同样设计了渲染到纹理,可以将一个纹理作为渲染对象,加入渲染器中,这时所有的渲染结果不再是屏幕,而是绑入渲染器的纹理。至于深度缓存什么的,也不必单独设置,只要在窗口建立之前,设置一个参数就可以了。当然,OpenGL在单独使用时,支持一次渲染到多个纹理,但sdl2目前并不支持。
渲染到SDL2纹理也很简单,一是建立纹理时需要单独声明,
gpRenderTexture = SDL_CreateTexture(gpSdlRender, SDL_PIXELFORMAT_ARGB8888,
SDL_TEXTUREACCESS_TARGET,RealWidth, RealHeight);
共有5个参数,第一个参数是所依托的渲染器指针,第二项是格式,ARGB 从高到低各占8个字节。第三项必须指明可以作为渲染目标,第四第五项是宽度和高度。
二是需要绑定
SDL_SetRenderTarget(gpSdlRender, rpRender);
如第二个参数为0 则去除绑定,所有输入将指向窗口。
七、向GLSL输入参数
总的说,GLSL参数着色器接收两类参数,一类是坐标,一类是常数(变量),这两类参数输入所用的方法是完全不一的,其中坐标输入有很多种方案可供选择,什么VBO,VAO等等,而且不同的OpenGL版本支持的方案也不完全相同,这里作为SDL2的应用,只介绍最直接,最简单的方案。
读取GLSL变量ID
顶点变量 ID
v_texcoordID = glGetAttribLocation(v_programID, "TexCoord");
v_vertexID = glGetAttribLocation(v_programID, "position");
常量ID
v_texID = glGetUniformLocation(v_programID, "v_tex");
v_tex1ID = glGetUniformLocation(v_programID, "v_tex1");
v_dataID = glGetUniformLocation(v_programID, "v_data");
使用
glUseProgram(v_programID);
glEnableVertexAttribArray(v_vertexID);
glEnableVertexAttribArray(v_texcoordID);
//数据
glUniform4fv(v_dataID, 20, (const float*)dataBuf);
//纹理ID
glUniform1i(v_texID, 0);
glUniform1i(v_tex1ID, 1);
其中datBuf是一个SDL_fColor数组,因为应用中要载入两个调色版和其他一些控制数据,将其放入一个数组中比较方便。
typedef struct SDL_fColor
{
GLfloat r;
GLfloat g;
GLfloat b;
GLfloat a;
} SDL_fColor;
sdl坐标与OpenGL坐标转换见后面的例子中。setRectToArr函数。
至此,在SDL2环境下运行OpenGL的GLSL着色器所有的环节已经介绍完成了,其实现顺序如下:
准备阶段:初始化SDL,运行SDL的OpenGL前置程序,建立SDL渲染器,运行GLAD或GLEW载入命令。初始阶段:编制顶点着色器,片元着色器,编译着色器,取着色器变量ID,准备SDL纹理。运行阶段:绑定纹理,对着色器赋值,运行glDrawArrays函数,然后运行SDL_GL_SwapWindow 或SDL_RenderPresent,最后清理。一个最简单的示例如下:
cmain.app
//cmain.cpp
#include "cmain.h"
const static GLchar* const vertexShader =
"#version 130\n\
in vec2 position;\n\
in vec2 TexCoord;\n\
out vec2 sTexCoord;\n\
void main()\n\
{\n\
gl_Position = vec4(position, 0.0, 1.0) ;\n\
sTexCoord = TexCoord ;\n\
}";
const static GLchar* const fragmentShader =
"#version 130\n\
uniform vec4 v_data[20];\n\
uniform sampler2D v_tex;\n\
uniform sampler2D v_tex1;\n\
in vec2 sTexCoord;\n\
out vec4 outColor;\n\
void main()\n\
{\n\
//数组v_data 0 color,1 mode\n\
//mode.x ==mode ,mode.y = alpha\n\
\n\
vec4 v_color = v_data[0];\n\
vec4 v_mode = v_data[1];\n\
\n\
vec4 t0 = texture(v_tex,sTexCoord);\n\
vec4 t1 = texture(v_tex1,sTexCoord);\n\
int x = int(v_mode.x +0.001);\n\
if(x == 0)\n\
//混合两个纹理\n\
{ \n\
outColor = t0 * v_mode.y + t1 * (1.0 - v_mode.y);\n\
outColor *= v_color;\n\
outColor.a = 1.0;\n\
}\n\
if(x == 1)\n\
//拷贝后乘Alpha\n\
{\n\
outColor = t0;\n\
outColor *= v_color;\n\
outColor *= v_mode.y;\n\
outColor.a = 1.0;\n\
}\n\
if(x == 3)\n\
//过滤全为零在点\n\
{\n\
if((t0.r == 0.0 && t0.g == 0.0 && t0.b == 0.0 && t0.a == 0.0) )\n\
{\
//抛弃 \n \
discard;\
}\n\
outColor = t0;\n\
outColor *= v_color;\n\
outColor *= v_mode.y;\n\
outColor.a = 1.0;\n\
}\n\
if(x == 6)\n\
//执行字模拷贝,用取得的纹理颜色值乘颜色\n\
{\n\
if((t0.r < 0.1 ) )\n\
{\
//抛弃 \n \
discard;\
}\n\
outColor = v_color;// * t0.r;\n\
outColor *= v_mode.y;\n\
outColor.a = 1.0;\n\
}\n\
if(x == 10.0)\n\
//翻转屏幕\n\
{\n\
outColor = texture(v_tex,vec2(sTexCoord.x,1.0 - sTexCoord.y));\n\
}\n\
}";
CMain::CMain()
{
glPreInit();
gpWindow = SDL_CreateWindow("SDL2 + Opengl 演示 ......",
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, WindowWidth, WindowHeight,
SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_HIDDEN);
SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengl");
gpSdlRender = SDL_CreateRenderer(gpWindow, -1, SDL_RENDERER_ACCELERATED);
#ifdef GLAD
//装入glad函数指针
if (!gladLoadGLLoader(SDL_GL_GetProcAddress))
exit(2);
#else
#ifdef GLEW
if (glewInit())
exit(2);
#endif
#endif
//creat GLSL SHADER
v_verticesID = loadShader(GL_VERTEX_SHADER, vertexShader);
v_fragmentID = loadShader(GL_FRAGMENT_SHADER, fragmentShader);
v_programID = compileProgram(v_verticesID, v_fragmentID);
v_texID = glGetUniformLocation(v_programID, "v_tex");
v_tex1ID = glGetUniformLocation(v_programID, "v_tex1");
v_dataID = glGetUniformLocation(v_programID, "v_data");
v_texcoordID = glGetAttribLocation(v_programID, "TexCoord");
v_vertexID = glGetAttribLocation(v_programID, "position");
SDL_Event evt;
//以下载入图片生成纹理
SDL_Surface* tst1 = SDL_LoadBMP("tst1.bmp");
SDL_Texture* txt1 = SDL_CreateTextureFromSurface(gpSdlRender, tst1);
SDL_FreeSurface(tst1);
SDL_ShowWindow(gpWindow);
RenderBlendCopy(gpRenderTexture, txt1);
RenderPresent(gpRenderTexture);
while (true)
{
while (SDL_PollEvent(&evt))
{
if (evt.type == SDL_QUIT)
{
SDL_DestroyTexture(txt1);
return;
}
}
SDL_Delay(10);
}
}
VOID CMain::RenderBlendCopy(SDL_Texture* rpRender, SDL_Texture* rpText1, SDL_Texture* rpText2, const WORD rAlpha, const WORD mode, const SDL_Color* rColor, const SDL_Rect* dstRect, const SDL_Rect* srcRect)
{
/*
模式功能
//mode = 0.0 混合 v_mode.y 混合因子
//mode = 1.0 拷贝后乘颜色
//mode = 2.0 置成单一颜色
//mode = 3.0 过滤拷贝全为零的点不拷贝
//mode = 4.0 与颜色混合,v_mode.y 混合因子
//mode = 6.0 显示字模
//mode = 10.0 实现显示反转
* */
{
SIZE msSize = { PictureWidth,PictureHeight };
if (rpText1)
{
SDL_QueryTexture(rpText1, 0, 0, (int*)&msSize.cx, (int*)&msSize.cy);
}
else if (rpText2)
SDL_QueryTexture(rpText1, 0, 0, (int*)&msSize.cx, (int*)&msSize.cy);
else
msSize = { 0,0 };
SIZE mdSize = { RealWidth,RealHeight };
if (rpRender)
{
SDL_QueryTexture(rpRender, 0, 0, (int*)&mdSize.cx, (int*)&mdSize.cy);
glViewport(0, 0, mdSize.cx, mdSize.cy);
}
else
{
SDL_Rect zView = { 0,0,0,0 };
SDL_GetWindowSize(gpWindow, &zView.w, &zView.h);
if (KeepAspectRatio && (fabs((double)zView.w / zView.h - 1.6) > 0.02))
{
double ra = (double)zView.w / zView.h - 1.6;
if (ra > 0)
{
zView.x = (zView.w - zView.h * 1.6) / 2;
zView.w -= zView.x * 2;
}
else
{
zView.y = (zView.h - zView.w / 1.6) / 2;
zView.h -= zView.y * 2;
}
}
glViewport(zView.x, zView.y, zView.w, zView.h);
}
setRectToArr(srcRect, dstRect, msSize, mdSize);
SDL_Color vColor = { 255,255,255,255 };
if (rColor == NULL)
rColor = &vColor;
else
rColor = rColor;
int err = 0;
err = glGetError() + err;
// 绑定渲染目标
if (SDL_SetRenderTarget(gpSdlRender, rpRender))
exit(9);
glActiveTexture(GL_TEXTURE0);
if (rpText1)
{
glEnable(GL_TEXTURE_2D);
glActiveTexture(GL_TEXTURE0);
SDL_GL_BindTexture(rpText1, 0, 0);
}
if (rpText2)
{
glEnable(GL_TEXTURE_2D);
glActiveTexture(GL_TEXTURE1);
SDL_GL_BindTexture(rpText2, 0, 0);
}
glUseProgram(v_programID);
glEnableVertexAttribArray(v_vertexID);
glEnableVertexAttribArray(v_texcoordID);
err = glGetError() + err;
glVertexAttribPointer(v_vertexID, 2, GL_FLOAT, GL_FALSE, 0, g_vertices);
glVertexAttribPointer(v_texcoordID, 2, GL_FLOAT, GL_FALSE, 0, g_texcoord);
err = glGetError() + err;
SDL_fColor dataBuf[20];
dataBuf[0] = { (GLfloat)(rColor->r * Div255),(GLfloat)(rColor->g * Div255)
,(GLfloat)(rColor->b * Div255),(GLfloat)(rColor->a * Div255) };
dataBuf[1] = { (GLfloat)(mode), (GLfloat)(rAlpha * Div255) ,0.0,0.0 };
glUniform4fv(v_dataID, 20, (const float*)dataBuf);
glUniform1i(v_texID, 0);
glUniform1i(v_tex1ID, 1);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glDisableVertexAttribArray(v_vertexID);
glDisableVertexAttribArray(v_texcoordID);
glActiveTexture(GL_TEXTURE0);
if (rpText1) SDL_GL_UnbindTexture(rpText1);
glActiveTexture(GL_TEXTURE1);
if (rpText2) SDL_GL_UnbindTexture(rpText2);
SDL_SetRenderTarget(gpSdlRender, 0);
glUseProgram(0);
glDisable(GL_TEXTURE_2D);
}
}
VOID CMain::RenderPresent(SDL_Texture* text)
{
RenderBlendCopy(NULL, text, NULL, 255, 10);
SDL_GL_SwapWindow(gpWindow);
//SDL_RenderPresent(gpSdlRender);
}
VOID CMain::glPreInit()
{
//要求在主窗口建立之前
//opengl 3.1
//SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
//SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
//SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); //设置多缓存的个数
//SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
//SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
}
cmain.h
#pragma once
#ifndef CMAIN_H
#define CMAIN_H
#define GLAD
//#define GLEW
#ifdef GLEW
#define GLEW_STATIC
#endif
#include <Windows.h>
#ifdef GLAD
#include <glad/glad.h>
#else
#ifdef GLEW
#include <GL/glew.h>
#else
#include <GL/GL.h>
#include <GL/GLU.h>
#define GL_GLEXT_PROTOTYPES
#include <SDL_opengl.h>
#include <SDL_opengl_glext.h>
#endif
#endif
#include <SDL.h>
using namespace std;
//系统基本显示画面尺度
#define PictureWidth 640
#define PictureHeight 400
#define RealWidth 640
#define RealHeight 400
#define WindowWidth 800
#define WindowHeight 500
const GLfloat Div255 = 1.0F / 255.0F;
//保持图片显示比例
const int KeepAspectRatio = 1;
typedef struct SDL_fColor
{
GLfloat r;
GLfloat g;
GLfloat b;
GLfloat a;
} SDL_fColor;
class CMain
{
public:
SDL_Window* gpWindow = NULL;
SDL_Renderer* gpSdlRender = NULL;
SDL_Texture* gpRenderTexture = NULL;
public:
CMain();
VOID RenderBlendCopy(SDL_Texture* rpRender, SDL_Texture* rpText1, SDL_Texture* rpText2 = NULL,
const WORD rAlpha = 255, const WORD mode = 1, const SDL_Color* rColor = NULL,
const SDL_Rect* dstRect = NULL, const SDL_Rect* srcRect = NULL);
VOID RenderPresent(SDL_Texture* text);
private:
VOID glPreInit();
GLfloat g_vertices[8] = {0};//位置顶点数组
GLfloat g_texcoord[8] = {0};//纹理顶点数组
GLuint v_verticesID = 0;//顶点ID
GLuint v_fragmentID = 0;//片断ID
GLuint v_programID = 0;//GLSL过程ID
GLint v_vertexID = 0; //位置顶点数组ID
GLint v_texcoordID = 0; //纹理顶点数组1ID
GLint v_texID = 0;//纹理1 ID
GLint v_tex1ID = 0;//纹理2 ID
GLint v_dataID = 0;//数据数组ID
private:
inline GLuint loadShader(GLenum shaderType, const GLchar* source)
{
GLuint shaderId = glCreateShader(shaderType);
glShaderSource(shaderId, 1, &source, NULL);
glCompileShader(shaderId);
GLint result = GL_FALSE;
glGetShaderiv(shaderId, GL_COMPILE_STATUS, &result);
return shaderId;
}
inline GLuint compileProgram(GLuint fragmentShaderId, GLuint vertexShaderId)
{
GLuint programId = glCreateProgram();
glAttachShader(programId, vertexShaderId);
glAttachShader(programId, fragmentShaderId);
glLinkProgram(programId);
return programId;
}
inline VOID setRectToArr(const SDL_Rect* src, const SDL_Rect* dst, const SIZE srcSize, const SIZE dstSize)
{
//转换成数组
GLfloat minx, miny, maxx, maxy;
GLfloat minu, maxu, minv, maxv;
if (dst && dst->w && dst->h && dstSize.cx && dstSize.cy)
{
minx = (GLfloat)dst->x / (GLfloat)dstSize.cx * 2.0F - 1.0F;
maxx = (GLfloat)(dst->x + dst->w) / (GLfloat)dstSize.cx * 2.0F - 1.0F;
miny = (GLfloat)dst->y / dstSize.cy * 2.0F - 1.0F;
maxy = (GLfloat)(dst->y + dst->h) / (GLfloat)dstSize.cy * 2.0F - 1.0F;
}
else
{
minx = -1.0f;
maxx = 1.0f;
miny = -1.0f;
maxy = 1.0f;
}
if (src && src->w && src->h && srcSize.cx && srcSize.cy)
{
minu = ((GLfloat)src->x / (GLfloat)srcSize.cx);
maxu = ((GLfloat)src->x + src->w) / (GLfloat)srcSize.cx;
minv = ((GLfloat)src->y / (GLfloat)srcSize.cy);
maxv = ((GLfloat)(src->y + src->h)) / (GLfloat)srcSize.cy;
}
else
{
minu = 0.0f;
maxu = 1.0f;
minv = 0.0f;
maxv = 1.0f;
}
g_vertices[0] = minx;
g_vertices[1] = miny;
g_vertices[2] = maxx;
g_vertices[3] = miny;
g_vertices[4] = maxx;
g_vertices[5] = maxy;
g_vertices[6] = minx;
g_vertices[7] = maxy;
g_texcoord[0] = minu;
g_texcoord[1] = minv;
g_texcoord[2] = maxu;
g_texcoord[3] = minv;
g_texcoord[4] = maxu;
g_texcoord[5] = maxv;
g_texcoord[6] = minu;
g_texcoord[7] = maxv;
}
};
#endif
main.app内容未变
好!今天就写到这,下一篇将介绍如何加入中文及特效的实现。
|