您的位置:首页 > 运维架构

OpenGL-- Shader 多个纹理 变换

2016-12-13 19:02 218 查看
源码参考:
http://learnopengl-cn.readthedocs.io/zh/latest/01%20Getting%20started/08%20Coordinate%20Systems/ http://blog.csdn.net/qq_28637193/article/details/52504590
此博用FreeImage加载纹理

1

主程序

// OpenGL 为状态机,数据为自上往下流动

#define GLEW_STATIC
#include <GL/glew.h>

#include <GLFW/glfw3.h>

#include <SOIL/SOIL.h>	// 用于加载纹理

// GLM Mathematics
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <iostream>

#include "Shader.h"

#pragma  comment(lib, "glfw3.lib")
#pragma  comment(lib, "glfw3dll.lib")
#pragma comment (lib, "glew32s.lib")

#pragma comment (lib, "opengl32.lib")
#pragma comment (lib, "glu32.lib")
#pragma comment (lib, "SOIL.lib")

// 键盘回调函数
void key_callback(GLFWwindow* pWnd, int key, int scancode, int action, int mode);

// 窗口大小
const GLuint WIDTH = 800, HEIGHT = 600;

// ***************************************************************************
int main()
{
// Init GLFW
glfwInit();
// Set all the required options for GLFW
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);

// 用 GLFW 的函数创建一个窗口  宽 高 名称 窗口模式(全屏/窗口nullptr)  共享上下文资源nullptr不共享
//GLFWmonitor* pMonitor = glfwGetPrimaryMonitor();	// 此两句为全屏模式
//GLFWwindow* pWnd = glfwCreateWindow(WIDTH, HEIGHT, "LearnOpenGL", pMonitor, nullptr);
GLFWwindow* pWnd = glfwCreateWindow(WIDTH, HEIGHT, "LearnOpenGL", nullptr, nullptr);
glfwMakeContextCurrent(pWnd);

// Set the required callback functions
glfwSetKeyCallback(pWnd, key_callback);	// 键盘回调

// GLEW 使用新方式检索函数指针和扩展(不太了解)
glewExperimental = GL_TRUE;	// 注释掉也能正常显示图形
// Initialize GLEW to setup the OpenGL Function pointers
glewInit();

glViewport(0, 0, WIDTH, HEIGHT);	// 设置视图尺寸
glEnable(GL_DEPTH_TEST);	// 启用深度测试

// 传入Shader源码位置, 加载Shader源码, 编译 链接 GLSL 程序
Shader ourShader("./vertexShader", "./fragementShader");

// Set up vertex data (and buffer(s)) and attribute pointers
// 立方体顶点 用三角形绘制  前三为位置坐标 后二为纹理坐标
GLfloat vertices[] = {
-0.5f, -0.5f, -0.5f,		0.0f, 0.0f,		0.5f, -0.5f, -0.5f,		1.0f, 0.0f,
0.5f,  0.5f, -0.5f,			1.0f, 1.0f,		0.5f,  0.5f, -0.5f,			1.0f, 1.0f,
-0.5f,  0.5f, -0.5f,		0.0f, 1.0f,		-0.5f, -0.5f, -0.5f,		0.0f, 0.0f,

-0.5f, -0.5f,  0.5f,		0.0f, 0.0f,		0.5f, -0.5f,  0.5f,			1.0f, 0.0f,
0.5f,  0.5f,  0.5f,			1.0f, 1.0f,		0.5f,  0.5f,  0.5f,			1.0f, 1.0f,
-0.5f,  0.5f,  0.5f,		0.0f, 1.0f,		-0.5f, -0.5f,  0.5f,		0.0f, 0.0f,

-0.5f,  0.5f,  0.5f,		1.0f, 0.0f,		-0.5f,  0.5f, -0.5f,		1.0f, 1.0f,
-0.5f, -0.5f, -0.5f,		0.0f, 1.0f,		-0.5f, -0.5f, -0.5f,		0.0f, 1.0f,
-0.5f, -0.5f,  0.5f,		0.0f, 0.0f,		-0.5f,  0.5f,  0.5f,		1.0f, 0.0f,

0.5f,  0.5f,  0.5f,			1.0f, 0.0f,		0.5f,  0.5f, -0.5f,			1.0f, 1.0f,
0.5f, -0.5f, -0.5f,		0.0f, 1.0f,		0.5f, -0.5f, -0.5f,		0.0f, 1.0f,
0.5f, -0.5f,  0.5f,			0.0f, 0.0f,		0.5f,  0.5f,  0.5f,			1.0f, 0.0f,

-0.5f, -0.5f, -0.5f,		0.0f, 1.0f,		0.5f, -0.5f, -0.5f,		1.0f, 1.0f,
0.5f, -0.5f,  0.5f,			1.0f, 0.0f,		0.5f, -0.5f,  0.5f,			1.0f, 0.0f,
-0.5f, -0.5f,  0.5f,		0.0f, 0.0f,		-0.5f, -0.5f, -0.5f,		0.0f, 1.0f,

-0.5f,  0.5f, -0.5f,		0.0f, 1.0f,		0.5f,  0.5f, -0.5f,			1.0f, 1.0f,
0.5f,  0.5f,  0.5f,			1.0f, 0.0f,		0.5f,  0.5f,  0.5f,			1.0f, 0.0f,
-0.5f,  0.5f,  0.5f,		0.0f, 0.0f,		-0.5f,  0.5f, -0.5f,		0.0f, 1.0f
};

// World space positions of our cubes 世界坐标系 点坐标   10个
glm::vec3 cubePositions[] = {
glm::vec3(-3.0f,  -1.0f,  -5.0f),			glm::vec3(2.0f,  5.0f, -1.0f),
glm::vec3(-1.5f, -2.2f, -2.5f),			glm::vec3(0.0f, -2.0f, -5.3f),
glm::vec3(2.4f, -0.4f, -3.5f),	  	    glm::vec3(-1.7f,  0.0f, -7.5f),
glm::vec3(1.3f, -2.0f, -2.5f),			glm::vec3(1.5f,  2.0f, -2.5f),
glm::vec3(1.5f,  0.2f, -1.5f),				glm::vec3(-1.3f,  1.0f, -1.5f)
};

GLuint VBO, VAO;
//生成一个VAO 并绑定
glGenVertexArrays(1, &VAO);	// Vertex Array Object VAO是所有顶点数据的状态集合
glBindVertexArray(VAO);			// VAO是一个容器,可以包括多个VBO

glGenBuffers(1, &VBO);		// Veretx Buffer Object
glBindBuffer(GL_ARRAY_BUFFER, VBO);		// VBO其实就是显卡中的显存
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); // 将顶点传至GPU

// void glVertexAttribPointer(GLuint index, GLint size, GLenum type,
//											GLboolean normalized, GLsizei stride, const void* pointer);
// 位置属性如何索引 //索引位置 大小  数据类型 是否要归一化 两个连续点间的偏移量 数据中第一个数据
// 第一个参数 index 指定要修改的顶点属性的索引值    被 Shader 中 layout (location = 0) 使用
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE,  5 * sizeof(GLfloat), (GLvoid*)0);
glEnableVertexAttribArray(0);	// 启用

// 纹理坐标属性
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE,  5 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
glEnableVertexAttribArray(2);

glBindVertexArray(0); // Unbind VAO	// 可注释

// ====================
// Texture 1	 纹理1
// ====================
GLuint textureA;
glGenTextures(1, &textureA);		// 生成一个纹理, textureA指向此纹理所在位置
// All upcoming GL_TEXTURE_2D operations now have effect on our texture object
glBindTexture(GL_TEXTURE_2D, textureA);	// 绑定此二维纹理,即接下来的纹理操作,都在 textureA 上

// 纹理坐标的第一维坐标值大于1.0或小于0.0时,应该如何处理  重复
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
// 纹理坐标的第二维坐标值大于1.0或小于0.0时,应该如何处理  重复
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
// 大纹理用于一个小面积物体上, 使用最接近的若干颜色,加权平均得到绘制的像素颜色
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
// 小纹理用于一个大面积物体上, 使用最接近的若干颜色,加权平均得到绘制的像素颜色
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

int width, height;
// 用 SOIL 库 加载图片
unsigned char* pChImg = SOIL_load_image("./box.jpg", &width, &height, 0, SOIL_LOAD_RGB);
//   载入纹理      目标  多层次细节(0不考虑) 颜色 宽 高      边框大小           数据类型           数据
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, pChImg);
glGenerateMipmap(GL_TEXTURE_2D);	//为与target相关联的纹理图像生成一组完整的mipmap
glBindTexture(GL_TEXTURE_2D, 0); // 解绑

SOIL_free_image_data(pChImg);		// 释放加载的图片内存

// ===================
// Texture 2 纹理2
// ===================
GLuint  textureB;
glGenTextures(1, &textureB);
glBindTexture(GL_TEXTURE_2D, textureB);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

pChImg = SOIL_load_image("./gold.jpg", &width, &height, 0, SOIL_LOAD_RGB);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, pChImg);
glGenerateMipmap(GL_TEXTURE_2D);
SOIL_free_image_data(pChImg);
glBindTexture(GL_TEXTURE_2D, 0);

while (!glfwWindowShouldClose(pWnd))
{
// 检查窗口事件
glfwPollEvents();

glClearColor(0.2f, 0.3f, 0.3f, 1.0f);	// 设置清除颜色
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);	// 用设置好的清除颜色设置窗口颜色

// Bind Textures using texture units
glActiveTexture(GL_TEXTURE0);		// 激活纹理A
glBindTexture(GL_TEXTURE_2D, textureA);		// 使用纹理A
// 获取着色器程序中,指定为uniform类型变量的id  即获取纹理采样器
GLint textureALocation = glGetUniformLocation(ourShader.getPrograme(), "textureA");
glUniform1i(textureALocation, 0);	// 将纹理A传至Shader的纹理采样器中

glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, textureB);
glUniform1i(glGetUniformLocation(ourShader.getPrograme(), "textureB"), 1);

ourShader.useShaderPrograme();		// 激活Shader程序

// Create transformations  创建旋转矩阵
glm::mat4 view;
glm::mat4 projection;
view = glm::translate(view, glm::vec3(0.0f, 0.0f, -3.0f));

// 投影矩阵				        	视野范围		         宽高比                 近平面     远平面
projection = glm::perspective(45.0f, (GLfloat)WIDTH / (GLfloat)HEIGHT, 0.1f, 100.0f);

// Get their uniform location
GLint modelLoc = glGetUniformLocation(ourShader.getPrograme(), "model");
GLint viewLoc = glGetUniformLocation(ourShader.getPrograme(), "view");
GLint projLoc = glGetUniformLocation(ourShader.getPrograme(), "projection");

// 将转换矩阵传至Shader
glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
// Note: currently we set the projection matrix each frame,
// but since the projection matrix rarely changes it's
// often best practice to set it outside the main loop only once.
glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projection));

glBindVertexArray(VAO);		// 绑定 VAO

for (GLuint i = 0; i <10; i++)		// 循环十次 绘制 每次绘制一个盒子
{
glm::mat4 model;
model = glm::translate(model, cubePositions[i]);
GLfloat angle = glfwGetTime();			// 随时间 变换角度旋转
model = glm::rotate(model, angle, glm::vec3(1.0f, 0.3f, 0.5f));
glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));

// 绘制三角形  从顶点数据 0 开始 绘制36个顶点   六面 每面两个三角形六个点 6*6 = 36
glDrawArrays(GL_TRIANGLES, 0, 36);
}

glBindVertexArray(0);		// 解绑 VAO

glfwSwapBuffers(pWnd);
}

// Properly de-allocate all resources once they've outlived their purpose
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);

glfwTerminate();		// 关闭并清理

return 0;
}

// Is called whenever a key is pressed/released via GLFW
void key_callback(GLFWwindow* pWnd, int key, int scancode, int action, int mode)
{
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)	// ESC键 退出
glfwSetWindowShouldClose(pWnd, GL_TRUE);
}


2

Shader处理

#pragma once

#ifndef TEXTURE_SHADER_H_
#define TEXTURE_SHADER_H_

#include <string>
#include <fstream>
#include <sstream>
#include <iostream>

#include <gl/glew.h>

#include <string>
#include <fstream>
#include <sstream>
#include <iostream>

#include <GL/glew.h>

class Shader
{
public:
Shader(const GLchar* vertexPath, const GLchar* fragmentPath);
~Shader();

public:
void useShaderPrograme();

GLuint getPrograme() {
return this->m_nProgram;
}

private:
GLuint  m_nProgram;
};

Shader::Shader(const GLchar* vertexPath, const GLchar* fragmentPath)
{
std::string vertexCode;
std::string fragmentCode;
std::ifstream vertexShaderF;
std::ifstream fragementShaderF;

vertexShaderF.exceptions(std::ifstream::badbit);
fragementShaderF.exceptions(std::ifstream::badbit);

try
{
vertexShaderF.open(vertexPath);		// 打开文件
fragementShaderF.open(fragmentPath);

std::stringstream vertexShaderStream, fragementShaderStream;
vertexShaderStream << vertexShaderF.rdbuf();	// 读取文件至stringstream中
fragementShaderStream << fragementShaderF.rdbuf();

vertexShaderF.close();
fragementShaderF.close();

vertexCode = vertexShaderStream.str();		// 转换成string类型
fragmentCode = fragementShaderStream.str();
}
catch (std::ifstream::failure e)
{
std::cout << "ERROR::SHADER::FILE_NOT_SUCCESSFULLY_READ:" << std::endl;
}

const GLchar* pVertexCode = vertexCode.c_str();	// string 转 char*
const GLchar* pFragementCode = fragmentCode.c_str();

GLuint nVertexShader, nFragementShader;
GLint nRes = 0;
GLchar chLogInfo[512] = { '\0' };

// 创建顶点着色器
nVertexShader = glCreateShader(GL_VERTEX_SHADER);
// 将顶点着色程序的源代码字符数组绑定到顶点着色器对象
glShaderSource(nVertexShader, 1, &pVertexCode, nullptr);
glCompileShader(nVertexShader); // compile shader 编译着色器

// 获取编译结果
glGetShaderiv(nVertexShader, GL_COMPILE_STATUS, &nRes);
if (!nRes)
{
glGetShaderInfoLog(nVertexShader, 512, nullptr, chLogInfo);
std::cout << "ERROR::SHADEF::VERTEX::COMPILATION_FAILED:" << chLogInfo << std::endl;
}

// 创建片断着色器
nFragementShader = glCreateShader(GL_FRAGMENT_SHADER);
// 将片段着色程序的源代码字符数组绑定到片段着色器对象
glShaderSource(nFragementShader, 1, &pFragementCode, nullptr);
glCompileShader(nFragementShader);
glGetShaderiv(nFragementShader, GL_COMPILE_STATUS, &nRes);
if (!nRes)
{
glGetShaderInfoLog(nFragementShader, 512, nullptr, chLogInfo);
std::cout << "ERROR::SHADEF::FRAGEMENT::COMPILATION_FAILED:" << chLogInfo << std::endl;
}

this->m_nProgram = glCreateProgram();	// 创建GLSL程序
glAttachShader(this->m_nProgram, nVertexShader);	// 绑定shader到program
glAttachShader(this->m_nProgram, nFragementShader);

// glLinkProgram操作产生最后的可执行程序,它包含最后可以在硬件上执行的硬件指令
glLinkProgram(this->m_nProgram);		// 链接
glGetProgramiv(this->m_nProgram, GL_LINK_STATUS, &nRes);
if (!nRes)
{
glGetProgramInfoLog(this->m_nProgram, 512, nullptr, chLogInfo);
std::cout << "ERROR::SHADEF::FRAGEMENT::LINK_FAILED:" << chLogInfo << std::endl;
}

glDeleteShader(nVertexShader);
glDeleteShader(nFragementShader);
}

Shader::~Shader()
{
}

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

void Shader::useShaderPrograme()
{
glUseProgram(this->m_nProgram);	// 使用porgram
}

#endif


3

vertexShader 顶点着色器

#version 330 core

// 位置值  在C++ 中 glVertexAttribPointer 指定
layout (location = 0) in vec3 position;
layout (location = 2) in vec2 texCoord;

// out: 指定变量为着色器阶段的一个输出
out vec2 TexCoord;

// Uniform前辍修饰的变量 初始值由外部程序赋值
//		指定这个值从应用程序传递个着色器
//		并在一个特定的图元中保持为常数值
//     在Shader中是只读的,只能由外部主机程序传入值
//		由顶点着色器和片段着色器 共享的,必须声明为全局变量
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

void main()
{
gl_Position = projection * view * model * vec4(position, 1.0f);
TexCoord = vec2(texCoord.x, 1.0 - texCoord.y);
}


4

fragementShader 片断着色器

#version 330 core

in vec2 TexCoord;

out vec4 color;

// sampler2D 采样器(Sampler)  内建数据类型
// 纹理采样
uniform sampler2D textureA;
uniform sampler2D textureB;

//
void main()
{
// texture内置函数 将与纹理坐标对应的纹理值从内存中取出来
// mix内置函数 将两个纹理一起混合,得到最后的颜色
color = mix(texture(textureA, TexCoord), texture(textureB, TexCoord), 0.3);
}


采用素材:





效果:  (静态截图, 实为动态) 



局部效果:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  OpenGL Shader