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://open.gl/textures

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();

}

}