5.2 Texture
Image from: http://www.wolf.org/wp-content/uploads/old_images/UploadImages/WolfLog/malik_9_29_08.JPG
https://alfonse.bitbucket.io/oldtut/Texturing/Tutorial%2014.html
http://www.opengl-tutorial.org/beginners-tutorials/tutorial-5-a-textured-cube
https://learnopengl.com/#!Getting-started/Textures
Complete Source Code:
Material.h
#ifndef MATERIAL_H
#define MATERIAL_H
#define GLEW_STATIC
#include <GL/glew.h>
#include <glm/glm.hpp>
/* defines the properties of a surface, will be applied to meshes */
/* some renderers use an "ambient" value, we wont ... */
/* instead the "diffuse" value will be used for ambient lighting */
struct Material
{
glm::vec3 Kd; /* diffuse, 0 .. 1 */
glm::vec3 Ks; /* specular, 0 .. 1 */
float Ns = 1.0f; /* shininess, 1 .. 1000 */
GLuint map_Kd = 0; /* diffuse texture */
/* etc ... */
};
#endif /* MATERIAL_H */
Graphics.cpp
#include "Graphics.h"
#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtx/transform.hpp>
#include <SOIL.h>
#include <iostream>
#include <vector>
#include <map>
#include "Shader.h"
#include "DrawCommands.h"
#include "Mesh.h"
#include "BufferData.h"
#include "Material.h"
using namespace std;
using namespace glm;
struct Vertex
{
vec3 Position;
vec3 Normal;
vec2 TexCoord;
};
uvec2 framebuffersize(0, 0);
GLuint program = 0;
GLuint vertexshader = 0;
GLuint fragmentshader = 0;
GLuint vertexarray = 0;
GLuint vertexbuffer = 0;
GLuint elementbuffer = 0;
DrawElementsBaseVertex drawelementsbasevertex_mesh;
/* collects multiple meshes, returns draw calls */
IndexedBufferData<Vertex> meshdata;
Mesh<Vertex> mesh;
Material material;
/* contains all GL texture objects */
map<string, GLuint> texture_map;
GLuint GetTexture(const string& filename)
{
/* check if texture already exists ... */
if (texture_map.find(filename) != texture_map.end())
return texture_map[filename];
/* load image */
int texture_width = 0;
int texture_height = 0;
int texture_channels = 0;
unsigned char* texture_data = SOIL_load_image(
filename.c_str(),
&texture_width, /* return width */
&texture_height, /* return height */
&texture_channels, /* return channels */
SOIL_LOAD_RGBA /* create texture with 4 components (RGBA) */
);
if (!texture_data)
{
cout << "ERROR: cant load texture " << filename << endl;
return 0;
}
/* generate new texture */
GLuint texture = 0;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, texture_width, texture_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, texture_data);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glBindTexture(GL_TEXTURE_2D, 0);
/* release texture memory */
SOIL_free_image_data(texture_data);
/* store GL texture somewhere */
texture_map[filename] = texture;
return texture;
}
bool Graphics::Initialize(unsigned int width, unsigned int height)
{
/* set view port */
Resize(width, height);
/* general settings */
glClearColor(0.3f, 0.3f, 0.3f, 0.0f); /* background gray */
glEnable(GL_DEPTH_TEST);
/* create all objects */
program = glCreateProgram();
vertexshader = glCreateShader(GL_VERTEX_SHADER);
fragmentshader = glCreateShader(GL_FRAGMENT_SHADER);
glGenVertexArrays(1, &vertexarray);
glGenBuffers(1, &vertexbuffer);
glGenBuffers(1, &elementbuffer);
/* shader source */
string vertexshader_source(
"#version 450 core\n"
"layout (location = 0) in vec3 in_position;"
"layout (location = 1) in vec3 in_normal;"
"layout (location = 2) in vec2 in_texcoord;"
"layout (location = 0) uniform mat4 Model = mat4(1);"
"layout (location = 4) uniform mat4 View = mat4(1);"
"layout (location = 8) uniform mat4 Projection = mat4(1);"
"out VS_FS {"
"smooth vec3 position;"
"smooth vec3 normal;"
"smooth vec2 texcoord;"
"} vs_out;"
"void main() {"
"mat4 MVP = Projection * View * Model;"
"gl_Position = MVP * vec4(in_position, 1);"
"vs_out.position = (Model * vec4(in_position, 1)).xyz;"
"vs_out.normal = (Model * vec4(in_normal, 0)).xyz;"
"vs_out.texcoord = in_texcoord;"
"}"
);
string fragmentshader_source(
"#version 450 core\n"
"layout (location = 12) uniform vec3 material_Kd = vec3(0, 0, 0);"
"layout (location = 13) uniform vec3 material_Ks = vec3(0, 0, 0);"
"layout (location = 14) uniform float material_Ns = 1.0f;"
"layout (binding = 1) uniform sampler2D map_Kd;"
"in VS_FS {"
"smooth vec3 position;"
"smooth vec3 normal;"
"smooth vec2 texcoord;"
"} fs_in;"
"layout (location = 0) out vec4 out_color;"
"void main() {"
"vec3 Kd = texture(map_Kd, fs_in.texcoord).rgb;"
"out_color = vec4(Kd, 1);"
"}"
);
/* compile shaders */
if (!CompileShader(vertexshader, vertexshader_source))
return false;
if (!CompileShader(fragmentshader, fragmentshader_source))
return false;
/* link program */
if (!LinkProgram(program, { vertexshader, fragmentshader }))
return false;
/* setup vertexarray */
glBindVertexArray(vertexarray);
glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<void*>(offsetof(Vertex, Position)));
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<void*>(offsetof(Vertex, Normal)));
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<void*>(offsetof(Vertex, TexCoord)));
glBindBuffer(GL_ARRAY_BUFFER, 0);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glEnableVertexAttribArray(2);
/* NOTE: the elementbuffer is also part of the vertex array */
/* while the VAO is bound, bind the element buffer */
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementbuffer);
glBindVertexArray(0);
/* anf after VAO is setup and unbound, savely unbind the elementbuffer again */
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
/* in OpenGL 4.5, there is a more "modern" way to do that */
/* glVertexArrayElementBuffer(vertexarray, elementbuffer); */
/* ... and another way to specify vertexbuffer: */
/* https://www.khronos.org/opengl/wiki/Vertex_Specification#Separate_attribute_format */
/* setup material */
material.Kd = vec3(0.3f, 0.4f, 0.8f);
material.Ks = vec3(1, 1, 1);
material.Ns = 1.0f;
material.map_Kd = GetTexture("malik_grizzer_shadow.jpg");
/* setup mesh */
mesh.Name = "triangle"; /* not really needed ... however */
mesh.Vertices.push_back({ { 0, 0, 0}, { 0, 0, 1 }, { 0, 1 } });
mesh.Vertices.push_back({ { 1, 0, 0}, { 0, 0, 1 }, { 1, 1 } });
mesh.Vertices.push_back({ { 1, 1, 0 },{ 0, 0, 1 }, { 1, 0 } });
mesh.Vertices.push_back({ { 0, 1, 0 },{ 0, 0, 1 }, { 0, 0 } });
/* first triangle */
mesh.Faces.push_back({});
mesh.Faces.back().VertexIndexA = 0;
mesh.Faces.back().VertexIndexB = 1;
mesh.Faces.back().VertexIndexC = 2;
/* second triangle */
mesh.Faces.push_back({});
mesh.Faces.back().VertexIndexA = 0;
mesh.Faces.back().VertexIndexB = 2;
mesh.Faces.back().VertexIndexC = 3;
/* the parameters for a "glDrawElements(...)" call */
drawelementsbasevertex_mesh = meshdata.AddMesh(mesh, GL_TRIANGLES);
/* get mesh data */
vector<Vertex> vertices = meshdata.Vertices();
vector<GLuint> indices = meshdata.Indices();
/* setup vertexbuffer */
glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * vertices.size(), vertices.data(), GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
/* setup elementbuffer */
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementbuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint) * indices.size(), indices.data(), GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
/* successfully initialized */
return true;
}
void Graphics::CleanUp()
{
/* destroy all objects */
glDeleteProgram(program);
glDeleteShader(vertexshader);
glDeleteShader(fragmentshader);
glDeleteVertexArrays(1, &vertexarray);
glDeleteBuffers(1, &vertexbuffer);
glDeleteBuffers(1, &elementbuffer);
/* delete all GL textures */
for (auto& texture_pair : texture_map)
glDeleteTextures(1, &texture_pair.second);
}
void Graphics::Render(const Scene& scene)
{
float aspectratio= (float)framebuffersize.x / framebuffersize.y;
mat4 View = scene.camera.View();
mat4 Projection = scene.camera.Projection(aspectratio);
/* view and projection matrix */
glProgramUniformMatrix4fv(program, 4, 1, GL_FALSE, value_ptr(View));
glProgramUniformMatrix4fv(program, 8, 1, GL_FALSE, value_ptr(Projection));
/* clear framebuffer */
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
/* draw triangles */
glUseProgram(program);
glBindVertexArray(vertexarray);
/* apply material */
glUniform3fv(12, 1, value_ptr(material.Kd));
glUniform3fv(13, 1, value_ptr(material.Ks));
glUniform1f(14, material.Ns);
/* apply texture */
/* map_Kd ==> texture unit 1 (see "binding" in fragmnt shader) */
glActiveTexture(GL_TEXTURE0 + 1);
glBindTexture(GL_TEXTURE_2D, material.map_Kd);
glActiveTexture(GL_TEXTURE0);
for (auto& object : scene.triangles)
{
mat4 Model = object.Transformation();
glUniformMatrix4fv(0, 1, GL_FALSE, value_ptr(Model));
/* render mesh */
Draw(drawelementsbasevertex_mesh);
}
glBindVertexArray(0);
glUseProgram(0);
CheckForGLError();
}
void Graphics::Resize(unsigned int width, unsigned int height)
{
framebuffersize = uvec2(width, height);
glViewport(0, 0, width, height);
}
GLContextInfo Graphics::GetContextInfos()
{
GLContextInfo infos;
glGetIntegerv(GL_MAJOR_VERSION, &infos.Version.Major);
glGetIntegerv(GL_MINOR_VERSION, &infos.Version.Minor);
infos.Version.Driver = (const char*)glGetString(GL_VERSION);
infos.Vendor = (const char*)glGetString(GL_VENDOR);
infos.Renderer = (const char*)glGetString(GL_RENDERER);
infos.Version.ShadingLanguage = (const char*)glGetString(GL_SHADING_LANGUAGE_VERSION);
GLint numberofextensions = 0;
glGetIntegerv(GL_NUM_EXTENSIONS, &numberofextensions);
for (int i = 0; i < numberofextensions; i++)
infos.SupportedExtensions.push_back((const char*)glGetStringi(GL_EXTENSIONS, i));
GLint numberofsupportedglslversions = 0;
glGetIntegerv(GL_NUM_SHADING_LANGUAGE_VERSIONS, &numberofsupportedglslversions);
for (int i = 0; i < numberofsupportedglslversions; i++)
infos.SupportedGLSLVersions.push_back((const char*)glGetStringi(GL_SHADING_LANGUAGE_VERSION, i));
return infos;
}
void Graphics::CheckForGLError()
{
for (GLenum error; (error = glGetError()) != GL_NO_ERROR;)
{
cout << "OpenGL Error: \t";
if (error == GL_INVALID_ENUM)
cout << "GL_INVALID_ENUM";
if (error == GL_INVALID_VALUE)
cout << "GL_INVALID_VALUE";
if (error == GL_INVALID_OPERATION)
cout << "GL_INVALID_OPERATION";
if (error == GL_STACK_OVERFLOW)
cout << "GL_STACK_OVERFLOW";
if (error == GL_STACK_UNDERFLOW)
cout << "GL_STACK_UNDERFLOW";
if (error == GL_OUT_OF_MEMORY)
cout << "GL_OUT_OF_MEMORY";
if (error == GL_INVALID_FRAMEBUFFER_OPERATION)
cout << "GL_INVALID_FRAMEBUFFER_OPERATION";
if (error == GL_CONTEXT_LOST)
cout << "GL_CONTEXT_LOST";
cout << (char)7 << endl; /*play sound*/
cin.get();
}
}