SDL2 + OPENGL GLSL 实践

一、准备

最近,闲来无事,研究了一下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 

int main()
{
    std::cout << "Hello World!\n";
}

二、第一个窗口

在SDL2上建立一个窗口是很简单的,由于是C++程序,我们需要新建一个类,点击项目->添加类

CMain 将main函数改成以下这个样子

main.app


#include "cmain.h"
#include 

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 

//系统基本显示画面尺度
#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 

#ifdef GLAD
#include 
#else

#ifdef GLEW
#include 
#else 

#include 
#include 
#define GL_GLEXT_PROTOTYPES
#include 
#include 
#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 

#ifdef GLAD
#include 
#else

#ifdef GLEW
#include 
#else 

#include 
#include 
#define GL_GLEXT_PROTOTYPES
#include 
#include 
#endif

#endif

#include 

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内容未变

好!今天就写到这,下一篇将介绍如何加入中文及特效的实现。

你可能感兴趣的:(qt,开发语言,c++)