智慧 + 毅力 = 无所不能

正确性、健壮性、可靠性、效率、易用性、可读性、可复用性、兼容性、可移植性...

导航

opengl读取灰度图生成三维地形并添加光照

Posted on 2019-11-05 11:23  Bill Yuan  阅读(739)  评论(0编辑  收藏  举报

转自:https://www.cnblogs.com/gucheng/p/10152889.html

准备第三方库 glew、freeglut、glm、opencv
准备一张灰度图
最终效果
代码如下
代码包括主程序源文件mainApp.cpp、顶点着色器shader.vs、片元着色器shader.fs

 mainApp.cpp

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <fstream>
#include <sstream>
#include <GL/glew.h> 
#include <GL/freeglut.h>  
#include <opencv2/core/core.hpp> 
#include <opencv2/highgui/highgui.hpp> 
#include <opencv2/imgproc/imgproc.hpp>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
 
using namespace std;
using namespace cv;
using namespace glm;
 
//shader文件
const char* vsShaderName = "shader.vs";//顶点着色器
const char* fsShaderName = "shader.fs";//片元着色器
 
GLuint VBO;//顶点缓冲对象
GLuint IBO;//索引缓冲对象
GLuint UVBO;//uv缓冲对象
GLuint TexBO;//贴图对象
GLuint NormalBO;//法线对象
//光照定义
struct {
    GLuint Color;//颜色
    GLuint AmbientIntensity;//环境光强度
    GLuint Direction;//方向
    GLuint DiffuseIntensity;//漫反射强度
} m_dirLightLocation;
static  GLfloat *vertices;
static unsigned int *indices;
static  GLfloat *uvs;
static GLfloat *normals;
 
GLuint ShaderProgram;
GLuint MatrixID;
GLuint MatrixID2;
GLuint TextureID;
 
int windowWidth = 1000;
int windowHeight = 800;
int imgWidth;
int imgHeihgt;
float verticeScale = 0.1f;
float yScale = 5.0f;
//相机参数
glm::mat4 ViewMatrix;//视图矩阵
glm::mat4 ProjectionMatrix; //投影矩阵
glm::mat4 MVP;//模型视图矩阵
glm::mat4 ModelMatrix;//模型矩阵
glm::vec3 position = glm::vec3(5, 5, 5); //相机位置
float horizontalAngle = 3.14f;
float verticalAngle = 0.0f;
float initialFoV = 45.0f; //相机视场角
float speed = 0.05f; //平移速度
float mouseSpeed = 0.005f;
int mouseX, mouseY;//鼠标位置 窗口坐标
bool mouseLeftDown = false;//鼠标左键按下
 
// 传递键盘事件
static void SpecialKeyboardCB(unsigned char Key, int x, int y)
{
    glm::vec3 direction(
        cos(verticalAngle) * sin(horizontalAngle),
        sin(verticalAngle),
        cos(verticalAngle) * cos(horizontalAngle)
    );
    glm::vec3 right = glm::vec3(
        sin(horizontalAngle - 3.14f / 2.0f),
        0,
        cos(horizontalAngle - 3.14f / 2.0f)
    );
    glm::vec3 up = glm::cross(right, direction);
 
    switch (Key) {
    case 'w':
        position += direction  * speed;
        //fprintf(stderr, "up \n");
        break;
    case 'd':
        position += right  * speed;
        //fprintf(stderr, "right \n");
        break;
    case 's':
        position -= direction  * speed;
        //fprintf(stderr, "down \n");
        break;
    case 'a':
        position -= right  * speed;
        //fprintf(stderr, "left \n");
        break;
    case 27:
        exit(1);
        break;
    default:
        break;
        //fprintf(stderr, "Unimplemented GLUT key\n");
        //exit(1);
    }
 
    float FoV = initialFoV;
    ProjectionMatrix = glm::perspective(glm::radians(FoV), (float)windowWidth / (float)windowHeight, 0.1f, 100.0f);
    ViewMatrix = glm::lookAt(
        position,
        position + direction,
        up
    );
    ModelMatrix = glm::mat4(1.0);
    MVP = ProjectionMatrix * ViewMatrix * ModelMatrix;
    glutPostRedisplay();//设置窗口重绘
}
 
//传递鼠标事件
void mouseCB(int button, int state, int x, int y)
{
    if (button == GLUT_LEFT_BUTTON)
    {
        if (state == GLUT_DOWN)
        {
            mouseLeftDown = true;
            mouseX = x;
            mouseY = y;
        }
        else if (state == GLUT_UP)
        {
            mouseLeftDown = false;
        }
    }
}
 
//传递鼠标位置
static void mouseMotionCB(int x, int y)
{
    if (mouseLeftDown == true)
    {
        horizontalAngle += mouseSpeed * float(x - mouseX);
        verticalAngle += mouseSpeed * float(y - mouseY);
        mouseX = x;
        mouseY = y;
        SpecialKeyboardCB(0, 0, 0);
    }
}
  
//渲染回调函数
void RenderScenceCB() {
    // 清空颜色缓存
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);<br>     //glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);//线框模式
    //传递矩阵数据
    glUniformMatrix4fv(MatrixID, 1, GL_FALSE, &MVP[0][0]);
    glUniformMatrix4fv(MatrixID2,1, GL_FALSE,&ModelMatrix[0][0]);
    //传递光照
    glUniform3f(m_dirLightLocation.Color, 1, 1, 0);
    glUniform1f(m_dirLightLocation.AmbientIntensity, 0.5);
    glUniform3f(m_dirLightLocation.Direction,1,1,0);
    glUniform1f(m_dirLightLocation.DiffuseIntensity,0.7);
    //传递顶点、索引、UV、法线
    glEnableVertexAttribArray(0); //开启顶点属性
    glBindBuffer(GL_ARRAY_BUFFER, VBO); //绑定GL_ARRAY_BUFFER缓冲器
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0); //告诉管线怎样解析bufer中的数据 
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO);
    glEnableVertexAttribArray(1);
    glBindBuffer(GL_ARRAY_BUFFER, UVBO);
    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0);
    glEnableVertexAttribArray(2);
    glBindBuffer(GL_ARRAY_BUFFER, NormalBO);
    glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, 0);
    //传递贴图纹理
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, TexBO);
    glUniform1i(TextureID, 0);
    //绘制
    glDrawElements(GL_TRIANGLES_ADJACENCY, (imgWidth - 1)*(imgHeihgt - 1) * 6 * 4, GL_UNSIGNED_SHORT, 0);
    glDisableVertexAttribArray(0);
    glDisableVertexAttribArray(1);
    glDisableVertexAttribArray(2);
    //交换前后缓存
    glutSwapBuffers();
}
 
//创建顶点
static void CreateVertexBuffer()
{
    //读取图片
    Mat img = imread("T2.png");
    int imgType = img.type();
    Mat resImg = Mat(img.rows, img.cols, imgType);
    resize(img, resImg, resImg.size(), 0, 0, INTER_LINEAR);
    Mat gImg = Mat(img.rows, img.cols, CV_8UC1); //灰度图
    cv::cvtColor(resImg, gImg, CV_BGR2GRAY);//bgr转灰度
    imgWidth = resImg.rows;
    imgHeihgt = resImg.cols;
    vertices = new GLfloat[imgWidth*imgHeihgt * 3];
    int k = 0;
    for (int i = 0; i < imgHeihgt; i++)
    {
        for (int j = 0; j < imgWidth; j++)
        {
            vertices[k++] = verticeScale* (float)i;
            int c = (int)gImg.at<uchar>(j, i);
            vertices[k++] = yScale*(float)c / 255.0f;
            vertices[k++] = verticeScale*(float)j;
        }
    }
    glGenBuffers(1, &VBO);
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, imgWidth*imgHeihgt * 3 * sizeof(GLfloat), vertices, GL_STATIC_DRAW);
}
//创建索引
static void CreateIndexBuffer()
{
    indices = new unsigned int[(imgWidth - 1)*(imgHeihgt - 1) * 6];
    int k = 0;
 
    for (int i = 0; i < imgHeihgt - 1; i++)
    {
        for (int j = 0; j < imgWidth - 1; j++)
        {
            indices[k++] = i*imgWidth + j;
            indices[k++] = i*imgWidth + j + 1;
            indices[k++] = i*imgWidth + j + imgWidth;
            indices[k++] = i*imgWidth + j + imgWidth;
            indices[k++] = i*imgWidth + j + 1;
            indices[k++] = i*imgWidth + j + imgWidth + 1;
        }
    }
    glGenBuffers(1, &IBO);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, (imgWidth - 1)*(imgHeihgt - 1) * 6 * sizeof(unsigned int), indices, GL_STATIC_DRAW);
  
}
//创建uv
static void CreateUVBuffer()
{
    uvs = new  GLfloat[imgWidth*imgHeihgt * 2];
    int k = 0;
    for (int i = 0; i < imgHeihgt; i++)
    {
        for (int j = 0; j < imgWidth; j++)
        {
            uvs[k++] = (float)i / (float)(imgHeihgt);
            uvs[k++] = (float)j / (float)(imgWidth);
        }
    }
    glGenBuffers(1, &UVBO);
    glBindBuffer(GL_ARRAY_BUFFER, UVBO);
    glBufferData(GL_ARRAY_BUFFER, imgWidth*imgHeihgt * 2 * sizeof(GLfloat), uvs, GL_STATIC_DRAW);
}
 
//创建贴图
static void CreateTexture()
{
    Mat img = imread("T2.png");
    Mat resImg = Mat(256, 256, img.type());
    resize(img, resImg, resImg.size(), 0, 0, INTER_LINEAR);
    cv::cvtColor(resImg, resImg, CV_BGR2RGB);
    glGenTextures(1, &TexBO);
    glBindTexture(GL_TEXTURE_2D, TexBO);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 256, 256, 0, GL_RGB, GL_UNSIGNED_BYTE, resImg.data);//设定纹理
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);//重复纹理
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);//滤波
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1);
    glGenerateMipmap(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, 0);
    TextureID = glGetUniformLocation(ShaderProgram, "myTexture");
}
 
//创建法线
static void CreateNormal()
{
    normals= new GLfloat[imgWidth*imgHeihgt*3];
    int k = 0;
    //遍历索引三角
    for (size_t i = 0; i < (imgWidth - 1)*(imgHeihgt - 1) * 6; i+=3)
    {
        static  unsigned int pIndex1 = indices[i];
        static  unsigned int pIndex2 = indices[i + 1];
        static  unsigned int pIndex3 = indices[i + 2];
        static  float x1 = vertices[pIndex1];
        static float y1 = vertices[pIndex1 + 1];
        static  float z1 = vertices[pIndex1 + 2];
        static  float x2 = vertices[pIndex2];
        static float y2 = vertices[pIndex2 + 1];
        static  float z2 = vertices[pIndex2 + 2];
        static  float x3 = vertices[pIndex3];
        static  float y3 = vertices[pIndex3 + 1];
        static  float z3 = vertices[pIndex3 + 2];
        //求边
        static  float vx1 = x2 - x1;
        static  float vy1 = y2 - y1;
        static  float vz1 = z2 - z1;
        static  float vx2 = x3 - x1;
        static  float vy2 = y3 - y1;
        static  float vz2 = z3 - z1;
        //叉乘求三角形法线
        static  float xN = vy1 * vz2 - vz1 *vy2;
        static  float yN = vz1 * vx2 - vx1 * vz2;
        static  float zN = vx1 * vy2 - vy1 * vx2;
        static  float Length = sqrtf(xN * xN + yN * yN + zN * zN);
        xN /= Length;
        yN /= Length;
        zN /= Length;
        //顶点法线更新
        normals[pIndex1] += xN;
        normals[pIndex1 + 1] += yN;
        normals[pIndex1 + 2] += zN;
        normals[pIndex2] += xN;
        normals[pIndex2 + 1] += yN;
        normals[pIndex2 + 2] += zN;
        normals[pIndex3] += xN;
        normals[pIndex3 + 1] += yN;
        normals[pIndex3 + 2] += zN;
    } 
    glGenBuffers(1, &NormalBO);
    glBindBuffer(GL_ARRAY_BUFFER, NormalBO);
    glBufferData(GL_ARRAY_BUFFER, imgWidth*imgHeihgt * 3 * sizeof(GLfloat), normals, GL_STATIC_DRAW);
}
 
// 使用shader文本编译shader对象,并绑定shader到着色器程序中
static void AddShader(GLuint ShaderProgram, const char* pShaderText, GLenum ShaderType)
{
    // 根据shader类型参数定义两个shader对象
    GLuint ShaderObj = glCreateShader(ShaderType);
    // 检查是否定义成功
    if (ShaderObj == 0) {
        fprintf(stderr, "Error creating shader type %d\n", ShaderType);
        exit(0);
    }
    // 定义shader的代码源
    const GLchar* p[1];
    p[0] = pShaderText;
    GLint Lengths[1];
    Lengths[0] = strlen(pShaderText);
    glShaderSource(ShaderObj, 1, p, Lengths);
    glCompileShader(ShaderObj);// 编译shader对象
                               // 检查和shader相关的错误
    GLint success;
    glGetShaderiv(ShaderObj, GL_COMPILE_STATUS, &success);
    if (!success) {
        GLchar InfoLog[1024];
        glGetShaderInfoLog(ShaderObj, 1024, NULL, InfoLog);
        fprintf(stderr, "Error compiling shader type %d: '%s'\n", ShaderType, InfoLog);
        exit(1);
    }
    // 将编译好的shader对象绑定到program object程序对象上
    glAttachShader(ShaderProgram, ShaderObj);
}
 
// 编译着色器函数
static void CompileShaders()
{
    // 创建着色器程序
    ShaderProgram = glCreateProgram();
    // 检查是否创建成功
    if (ShaderProgram == 0) {
        fprintf(stderr, "Error creating shader program\n");
        exit(1);
    }
    // 存储着色器文本的字符串
    string vs, fs;
    // 分别读取着色器文件中的文本到字符串
    std::ifstream VertexShaderStream(vsShaderName, std::ios::in);
    if (VertexShaderStream.is_open()) {
        std::stringstream sstr;
        sstr << VertexShaderStream.rdbuf();
        vs = sstr.str();
        VertexShaderStream.close();
    }
    else {
        printf("Error to open %s\n", vsShaderName);
        getchar();
        exit(0);
    }
    std::ifstream FragmentShaderStream(fsShaderName, std::ios::in);
    if (FragmentShaderStream.is_open()) {
        std::stringstream sstr;
        sstr << FragmentShaderStream.rdbuf();
        fs = sstr.str();
        FragmentShaderStream.close();
    }
 
    // 添加顶点着色器和片段着色器
    AddShader(ShaderProgram, vs.c_str(), GL_VERTEX_SHADER);
    AddShader(ShaderProgram, fs.c_str(), GL_FRAGMENT_SHADER);
    // 链接shader着色器程序,并检查程序相关错误
    GLint Success = 0;
    GLchar ErrorLog[1024] = { 0 };
    glLinkProgram(ShaderProgram);
    glGetProgramiv(ShaderProgram, GL_LINK_STATUS, &Success);
    if (Success == 0) {
        glGetProgramInfoLog(ShaderProgram, sizeof(ErrorLog), NULL, ErrorLog);
        fprintf(stderr, "Error linking shader program: '%s'\n", ErrorLog);
        exit(1);
    }
    // 检查验证在当前的管线状态程序是否可以被执行
    glValidateProgram(ShaderProgram);
    glGetProgramiv(ShaderProgram, GL_VALIDATE_STATUS, &Success);
    if (!Success) {
        glGetProgramInfoLog(ShaderProgram, sizeof(ErrorLog), NULL, ErrorLog);
        fprintf(stderr, "Invalid shader program: '%s'\n", ErrorLog);
        exit(1);
    }
    glUseProgram(ShaderProgram);
    //统一变量位置
    MatrixID = glGetUniformLocation(ShaderProgram, "gWVP");
    MatrixID2= glGetUniformLocation(ShaderProgram, "gWorld");
    m_dirLightLocation.Color = glGetUniformLocation(ShaderProgram,"gDirectionalLight.Color");
    m_dirLightLocation.AmbientIntensity = glGetUniformLocation(ShaderProgram,"gDirectionalLight.AmbientIntensity");
    m_dirLightLocation.Direction = glGetUniformLocation(ShaderProgram,"gDirectionalLight.Direction");
    m_dirLightLocation.DiffuseIntensity = glGetUniformLocation(ShaderProgram,"gDirectionalLight.DiffuseIntensity");
}
 
int main(int argc, char ** argv) {
    // 初始化GLUT
    glutInit(&argc, argv);
    // 显示模式:双缓冲、RGBA
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);
    // 窗口设置
    glutInitWindowSize(windowWidth, windowHeight);      // 窗口尺寸
    glutInitWindowPosition(100, 100);  // 窗口位置
    glutCreateWindow("terrainTest2");   // 窗口标题
 
    GLenum res = glewInit();
    if (res != GLEW_OK) {
        fprintf(stderr, "Error: '%s'\n", glewGetErrorString(res));
        return 1;
    }
 
    // 开始渲染
    glutDisplayFunc(RenderScenceCB);
    // 注册键盘事件
    glutKeyboardFunc(SpecialKeyboardCB);
    //注册鼠标事件
    glutMouseFunc(mouseCB);
    glutMotionFunc(mouseMotionCB);
    mouseX = windowWidth / 2;
    mouseY = windowHeight / 2;
    // 缓存清空后的颜色值
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
    //创建顶点
    CreateVertexBuffer();
    //创建索引
    CreateIndexBuffer();
    //创建uv
    CreateUVBuffer();
    //创建贴图
    CreateTexture();
    //创建法线
    CreateNormal();
    // 编译着色器
    CompileShaders();
    //开启深度测试
    glEnable(GL_DEPTH_TEST);
    // 通知开始GLUT的内部循环
    glutMainLoop();
    delete vertices;
    return 0;
}

shader.vs

#version 330
layout (location = 0) in vec3 Position;
layout (location = 1) in vec2 vertexUV;
layout (location = 2) in vec3 Normal;
uniform mat4 gWVP;
uniform mat4 gWorld;
out vec2 UV;
out vec3 Normal0;
void main()
{
    gl_Position = gWVP * vec4(Position, 1.0);
    UV = vertexUV;
    Normal0 = (gWorld * vec4(Normal, 0.0)).xyz;
}

shader.fs

#version 330
in vec2 UV;
in vec3 Normal0;   
out vec4 FragColor;  
//光照
struct DirectionalLight                                                            
{                                                                                  
    vec3 Color;                                                                    
    float AmbientIntensity;                                                        
    float DiffuseIntensity;                                                        
    vec3 Direction;                                                                
};                                                                                  
uniform DirectionalLight gDirectionalLight;      
uniform sampler2D myTexture;
void main()
{ 
//环境光
    vec4 AmbientColor = vec4(gDirectionalLight.Color, 1.0f) *                      
                        gDirectionalLight.AmbientIntensity;                        
 //漫反射                                                                                  
    float DiffuseFactor = dot(normalize(Normal0), -gDirectionalLight.Direction);    
    vec4 DiffuseColor;                                                               
    if (DiffuseFactor > 0) {                                                       
        DiffuseColor = vec4(gDirectionalLight.Color, 1.0f) *                       
                       gDirectionalLight.DiffuseIntensity *                        
                       DiffuseFactor;                                              
    }                                                                              
    else {                                                                         
        DiffuseColor = vec4(0, 0, 0, 0);                                           
    }                                                                               
    FragColor = texture2D(myTexture, UV.xy) *                                
                (AmbientColor + DiffuseColor);   
     
}