Chapter 6: Building an Abstract Renderer
In order to avoid getting caught up in any specific graphics APIs, in this chapter, you will build an abstraction layer on top of OpenGL.
本章没啥重点内容,就是OpenGL的那些东西,稍微新一点的几个OpenGL的api,之前没使用过:
glGetActiveUniform glGetActiveAttrib
Shader类
定义Shader头文件
头文件如下:
#ifndef _H_SHADER_
#define _H_SHADER_
#include <map>
#include <string>
class Shader
{
private:
unsigned int mHandle;
std::map<std::string, unsigned int> mAttributes;
std::map<std::string, unsigned int> mUniforms;
private:
std::string ReadFile(const std::string& path);
unsigned int CompileVertexShader(const std::string& vertex);
unsigned int CompileFragmentShader(const std::string& fragment);
bool LinkShaders(unsigned int vertex, unsigned int fragment);
void PopulateAttributes();
void PopulateUniforms();
private:
Shader(const Shader&);
Shader& operator=(const Shader&);
public:
Shader();
Shader(const std::string& vertex, const std::string& fragment);
~Shader();
void Load(const std::string& vertex, const std::string& fragment);
void Bind();
void UnBind();
unsigned int GetAttribute(const std::string& name);
unsigned int GetUniform(const std::string& name);
unsigned int GetHandle();
};
#endif
Get All Uniforms And Attributes
一个Shader对象代表一个Shader Program,读取文件、编译和Link Shader都是我做过好多次的东西,这次唯一没做过的就是获取一个Shader Program的所有Uniforms和Attributes,存到Shader的两个map里,Shader的uniforms我比较清楚,而Attributes应该是使用glVertexArrayAttrib分配的顶点属性。
相关部分的代码如下:
class Shader
{
std::map<std::string, unsigned int> mAttributes;
std::map<std::string, unsigned int> mUniforms;
unsigned int GetAttribute(const std::string& name)
{
std::map<std::string, unsigned int>::iterator it = mAttributes.find(name);
if (it == mAttributes.end())
{
std::cout << "Retrieving bad attribute index: " << name << "\n";
return 0;
}
return it->second;
}
unsigned int GetUniform(const std::string& name)
{
std::map<std::string, unsigned int>::iterator it = mUniforms.find(name);
if (it == mUniforms.end())
{
std::cout << "Retrieving bad uniform index: " << name << "\n";
return 0;
}
return it->second;
}
void PopulateAttributes()
{
int count = -1;
int length;
char name[128];
int size;
GLenum type;
glUseProgram(mHandle);
glGetProgramiv(mHandle, GL_ACTIVE_ATTRIBUTES, &count);
for (int i = 0; i < count; ++i)
{
memset(name, 0, sizeof(char) * 128);
glGetActiveAttrib(mHandle, (GLuint)i, 128, &length, &size, &type, name);
int attrib = glGetAttribLocation(mHandle, name);
if (attrib >= 0)
mAttributes[name] = attrib;
}
glUseProgram(0);
}
void PopulateUniforms()
{
int count = -1;
int length;
char name[128];
int size;
GLenum type;
char testName[256];
glUseProgram(mHandle);
glGetProgramiv(mHandle, GL_ACTIVE_UNIFORMS, &count);
for (int i = 0; i < count; ++i)
{
memset(name, 0, sizeof(char) * 128);
glGetActiveUniform(mHandle, (GLuint)i, 128, &length, &size, &type, name);
int uniform = glGetUniformLocation(mHandle, name);
if (uniform >= 0)
{
std::string uniformName = name;
std::size_t found = uniformName.find('[');
if (found != std::string::npos)
{
uniformName.erase(uniformName.begin() + found, uniformName.end());
unsigned int uniformIndex = 0;
while (true)
{
memset(testName, 0, sizeof(char) * 256);
sprintf(testName, "%s[%d]", uniformName.c_str(), uniformIndex++);
int uniformLocation = glGetUniformLocation(mHandle, testName);
if (uniformLocation < 0)
break;
mUniforms[testName] = uniformLocation;
}
}
mUniforms[uniformName] = uniform;
}
}
glUseProgram(0);
}
}
cpp文件
#define _CRT_SECURE_NO_WARNINGS
#include "Shader.h"
#include "glad.h"
#include <fstream>
#include <sstream>
#include <iostream>
Shader::Shader()
{
mHandle = glCreateProgram();
}
Shader::Shader(const std::string& vertex, const std::string& fragment)
{
mHandle = glCreateProgram();
Load(vertex, fragment);
}
Shader::~Shader()
{
glDeleteProgram(mHandle);
}
std::string Shader::ReadFile(const std::string& path)
{
std::ifstream file;
file.open(path);
std::stringstream contents;
contents << file.rdbuf();
file.close();
return contents.str();
}
unsigned int Shader::CompileVertexShader(const std::string& vertex)
{
unsigned int v_shader = glCreateShader(GL_VERTEX_SHADER);
const char* v_source = vertex.c_str();
glShaderSource(v_shader, 1, &v_source, NULL);
glCompileShader(v_shader);
int success = 0;
glGetShaderiv(v_shader, GL_COMPILE_STATUS, &success);
if (!success)
{
char infoLog[512];
glGetShaderInfoLog(v_shader, 512, NULL, infoLog);
std::cout << "ERROR: Vertex compilation failed.\n";
std::cout << "\t" << infoLog << "\n";
glDeleteShader(v_shader);
return 0;
};
return v_shader;
}
unsigned int Shader::CompileFragmentShader(const std::string& fragment)
{
unsigned int f_shader = glCreateShader(GL_FRAGMENT_SHADER);
const char* f_source = fragment.c_str();
glShaderSource(f_shader, 1, &f_source, NULL);
glCompileShader(f_shader);
int success = 0;
glGetShaderiv(f_shader, GL_COMPILE_STATUS, &success);
if (!success)
{
char infoLog[512];
glGetShaderInfoLog(f_shader, 512, NULL, infoLog);
std::cout << "ERROR: Fragment compilation failed.\n";
std::cout << "\t" << infoLog << "\n";
glDeleteShader(f_shader);
return 0;
};
return f_shader;
}
bool Shader::LinkShaders(unsigned int vertex, unsigned int fragment)
{
glAttachShader(mHandle, vertex);
glAttachShader(mHandle, fragment);
glLinkProgram(mHandle);
int success = 0;
glGetProgramiv(mHandle, GL_LINK_STATUS, &success);
if (!success)
{
char infoLog[512];
glGetProgramInfoLog(mHandle, 512, NULL, infoLog);
std::cout << "ERROR: Shader linking failed.\n";
std::cout << "\t" << infoLog << "\n";
glDeleteShader(vertex);
glDeleteShader(fragment);
return false;
}
glDeleteShader(vertex);
glDeleteShader(fragment);
return true;
}
void Shader::PopulateAttributes()
{
int count = -1;
int length;
char name[128];
int size;
GLenum type;
glUseProgram(mHandle);
glGetProgramiv(mHandle, GL_ACTIVE_ATTRIBUTES, &count);
for (int i = 0; i < count; ++i)
{
memset(name, 0, sizeof(char) * 128);
glGetActiveAttrib(mHandle, (GLuint)i, 128, &length, &size, &type, name);
int attrib = glGetAttribLocation(mHandle, name);
if (attrib >= 0)
mAttributes[name] = attrib;
}
glUseProgram(0);
}
void Shader::PopulateUniforms()
{
int count = -1;
int length;
char name[128];
int size;
GLenum type;
char testName[256];
glUseProgram(mHandle);
glGetProgramiv(mHandle, GL_ACTIVE_UNIFORMS, &count);
for (int i = 0; i < count; ++i)
{
memset(name, 0, sizeof(char) * 128);
glGetActiveUniform(mHandle, (GLuint)i, 128, &length, &size, &type, name);
int uniform = glGetUniformLocation(mHandle, name);
if (uniform >= 0)
{
std::string uniformName = name;
std::size_t found = uniformName.find('[');
if (found != std::string::npos)
{
uniformName.erase(uniformName.begin() + found, uniformName.end());
unsigned int uniformIndex = 0;
while (true)
{
memset(testName, 0, sizeof(char) * 256);
sprintf(testName, "%s[%d]", uniformName.c_str(), uniformIndex++);
int uniformLocation = glGetUniformLocation(mHandle, testName);
if (uniformLocation < 0)
break;
mUniforms[testName] = uniformLocation;
}
}
mUniforms[uniformName] = uniform;
}
}
glUseProgram(0);
}
void Shader::Load(const std::string& vertex, const std::string& fragment)
{
std::ifstream f(vertex.c_str());
bool vertFile = f.good();
f.close();
f = std::ifstream(vertex.c_str());
bool fragFile = f.good();
f.close();
std::string v_source = vertex;
if (vertFile)
v_source = ReadFile(vertex);
std::string f_source = fragment;
if (fragFile)
f_source = ReadFile(fragment);
unsigned int v_shader = CompileVertexShader(v_source);
unsigned int f_shader = CompileFragmentShader(f_source);
if (LinkShaders(v_shader, f_shader))
{
PopulateAttributes();
PopulateUniforms();
}
}
void Shader::Bind()
{
glUseProgram(mHandle);
}
void Shader::UnBind()
{
glUseProgram(0);
}
unsigned int Shader::GetHandle()
{
return mHandle;
}
unsigned int Shader::GetAttribute(const std::string& name)
{
std::map<std::string, unsigned int>::iterator it = mAttributes.find(name);
if (it == mAttributes.end())
{
std::cout << "Retrieving bad attribute index: " << name << "\n";
return 0;
}
return it->second;
}
unsigned int Shader::GetUniform(const std::string& name)
{
std::map<std::string, unsigned int>::iterator it = mUniforms.find(name);
if (it == mUniforms.end())
{
std::cout << "Retrieving bad uniform index: " << name << "\n";
return 0;
}
return it->second;
}
Working with buffers (attributes)
A vertex is made up of attributes. For example, a vertex has a position and a normal, which are both attributes.
The Attribute class declaration
创建Attribute.h 文件:
#ifndef _H_ATTRIBUTE_
#define _H_ATTRIBUTE_
#include <vector>
template<typename T>
class Attribute
{
protected:
unsigned int mHandle;
unsigned int mCount;
private:
Attribute(const Attribute& other);
Attribute& operator=(const Attribute& other);
void SetAttribPointer(unsigned int slot);
public:
Attribute();
~Attribute();
void Set(T* inputArray, unsigned int arrayLength);
void Set(std::vector<T>& input);
void BindTo(unsigned int slot);
void UnBindFrom(unsigned int slot);
unsigned int Count();
unsigned int GetHandle();
};
#endif
Implementing the Attribute class
#include "Attribute.h"
#include "glad.h"
#include "vec2.h"
#include "vec3.h"
#include "vec4.h"
#include "quat.h"
template Attribute<int>;
template Attribute<float>;
template Attribute<vec2>;
template Attribute<vec3>;
template Attribute<vec4>;
template Attribute<ivec4>;
template Attribute<quat>;
template<typename T>
Attribute<T>::Attribute()
{
glGenBuffers(1, &mHandle);
mCount = 0;
}
template<typename T>
Attribute<T>::~Attribute()
{
glDeleteBuffers(1, &mHandle);
}
template<typename T>
unsigned int Attribute<T>::Count()
{
return mCount;
}
template<typename T>
unsigned int Attribute<T>::GetHandle()
{
return mHandle;
}
template<typename T>
void Attribute<T>::Set(T* inputArray, unsigned int arrayLength)
{
mCount = arrayLength;
unsigned int size = sizeof(T);
glBindBuffer(GL_ARRAY_BUFFER, mHandle);
glBufferData(GL_ARRAY_BUFFER, size * mCount, inputArray, GL_STREAM_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
template<typename T>
void Attribute<T>::Set(std::vector<T>& input)
{
Set(&input[0], (unsigned int)input.size());
}
template<>
void Attribute<int>::SetAttribPointer(unsigned int slot)
{
glVertexAttribIPointer(slot, 1, GL_INT, 0, (void*)0);
}
template<>
void Attribute<ivec4>::SetAttribPointer(unsigned int slot)
{
glVertexAttribIPointer(slot, 4, GL_INT, 0, (void*)0);
}
template<>
void Attribute<float>::SetAttribPointer(unsigned int slot)
{
glVertexAttribPointer(slot, 1, GL_FLOAT, GL_FALSE, 0, (void*)0);
}
template<>
void Attribute<vec2>::SetAttribPointer(unsigned int slot)
{
glVertexAttribPointer(slot, 2, GL_FLOAT, GL_FALSE, 0, (void*)0);
}
template<>
void Attribute<vec3>::SetAttribPointer(unsigned int slot)
{
glVertexAttribPointer(slot, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
}
template<>
void Attribute<vec4>::SetAttribPointer(unsigned int slot)
{
glVertexAttribPointer(slot, 4, GL_FLOAT, GL_FALSE, 0, (void*)0);
}
template<>
void Attribute<quat>::SetAttribPointer(unsigned int slot)
{
glVertexAttribPointer(slot, 4, GL_FLOAT, GL_FALSE, 0, (void*)0);
}
template<typename T>
void Attribute<T>::BindTo(unsigned int slot)
{
glBindBuffer(GL_ARRAY_BUFFER, mHandle);
glEnableVertexAttribArray(slot);
SetAttribPointer(slot);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
template<typename T>
void Attribute<T>::UnBindFrom(unsigned int slot)
{
glBindBuffer(GL_ARRAY_BUFFER, mHandle);
glDisableVertexAttribArray(slot);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
创建uniform类
前面的Attribute类是有实例化的对象的,代表每个顶点的数据,这里的Uniform类也是个模板类,但是没有实例化对象,只有public static函数,因为它里面的数据是所有顶点共享的。
对于每种uniform的数据类型T而言,需要设置三个函数:
- 设置单独的一个uniform,设置一个T的值
- 设置T对应的uniform的数组的值
- 与2作用相同,但是接受的是vector数组(for convenience)
Uniform.h 如下:
#ifndef _H_UNIFORM_
#define _H_UNIFORM_
#include <vector>
template <typename T>
class Uniform
{
private:
Uniform();
Uniform(const Uniform&);
Uniform& operator=(const Uniform&);
~Uniform();
public:
static void Set(unsigned int slot, const T& value);
static void Set(unsigned int slot, T* inputArray, unsigned int arrayLength);
static void Set(unsigned int slot, std::vector<T>& inputArray);
};
#endif
Uniform.cpp 如下:
#include "Uniform.h"
#include "glad.h"
#include "vec2.h"
#include "vec3.h"
#include "vec4.h"
#include "quat.h"
#include "mat4.h"
template Uniform<int>;
template Uniform<ivec4>;
template Uniform<ivec2>;
template Uniform<float>;
template Uniform<vec2>;
template Uniform<vec3>;
template Uniform<vec4>;
template Uniform<quat>;
template Uniform<mat4>;
#define UNIFORM_IMPL(gl_func, tType, dType) \
template<> \
void Uniform<tType>::Set(unsigned int slot, tType* data, unsigned int length) { \
gl_func(slot, (GLsizei)length, (dType*)&data[0]); \
}
UNIFORM_IMPL(glUniform1iv, int, int)
UNIFORM_IMPL(glUniform4iv, ivec4, int)
UNIFORM_IMPL(glUniform2iv, ivec2, int)
UNIFORM_IMPL(glUniform1fv, float, float)
UNIFORM_IMPL(glUniform2fv, vec2, float)
UNIFORM_IMPL(glUniform3fv, vec3, float)
UNIFORM_IMPL(glUniform4fv, vec4, float)
UNIFORM_IMPL(glUniform4fv, quat, float)
template<>
void Uniform<mat4>::Set(unsigned int slot, mat4* inputArray, unsigned int arrayLength)
{
glUniformMatrix4fv(slot, (GLsizei)arrayLength, false, (float*)&inputArray[0]);
}
template <typename T>
void Uniform<T>::Set(unsigned int slot, const T& value)
{
Set(slot, (T*)&value, 1);
}
template <typename T>
void Uniform<T>::Set(unsigned int slot, std::vector<T>& value)
{
Set(slot, &value[0], (unsigned int)value.size());
}
没啥稀奇的,我记得之前看的OpenGL教程里是把这块内容放到了Shader类里,这本书的代码里把它单独拆出来放到了Uniform类里。
创建Index Buffer类
与Attribute类类似:
#ifndef _H_INDEXBUFFER_
#define _H_INDEXBUFFER_
#include <vector>
class IndexBuffer
{
public:
unsigned int mHandle;
unsigned int mCount;
private:
IndexBuffer(const IndexBuffer& other);
IndexBuffer& operator=(const IndexBuffer& other);
public:
IndexBuffer();
~IndexBuffer();
void Set(unsigned int* inputArray, unsigned int arrayLengt);
void Set(std::vector<unsigned int>& input);
unsigned int Count();
unsigned int GetHandle();
};
#endif
#include "IndexBuffer.h"
#include "glad.h"
IndexBuffer::IndexBuffer()
{
glGenBuffers(1, &mHandle);
mCount = 0;
}
IndexBuffer::~IndexBuffer()
{
glDeleteBuffers(1, &mHandle);
}
unsigned int IndexBuffer::Count()
{
return mCount;
}
unsigned int IndexBuffer::GetHandle()
{
return mHandle;
}
void IndexBuffer::Set(unsigned int* inputArray, unsigned int arrayLengt)
{
mCount = arrayLengt;
unsigned int size = sizeof(unsigned int);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mHandle);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, size * mCount, inputArray, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
void IndexBuffer::Set(std::vector<unsigned int>& input)
{
Set(&input[0], (unsigned int)input.size());
}
Rendering geometry
作者划分的也太细了,居然还有个Draw类(感觉叫Renderer类不是更好么),讲道理感觉封装的过于细致了,没必要,Draw类就是把OpenGL的四个Draw Call函数给封装了起来,就是这四个:
glDrawArrays : 直接按输入的Primitive类型绘制,不需要Index BufferglDrawElements : 按照输入的Primitive类型和Index Buffer绘制glDrawArraysInstanced glDrawElementsInstanced
代码如下:
#ifndef _H_DRAW_
#define _H_DRAW_
#include "IndexBuffer.h"
enum class DrawMode
{
Points,
LineStrip,
LineLoop,
Lines,
Triangles,
TriangleStrip,
TriangleFan
};
void Draw(IndexBuffer& inIndexBuffer, DrawMode mode);
void Draw(unsigned int vertexCount, DrawMode mode);
void DrawInstanced(IndexBuffer& inIndexBuffer, DrawMode mode, unsigned int instanceCount);
void DrawInstanced(unsigned int vertexCount, DrawMode mode, unsigned int numInstances);
#endif
#include "Draw.h"
#include "glad.h"
#include <iostream>
static GLenum DrawModeToGLEnum(DrawMode input)
{
switch (input)
{
case DrawMode::Points:
return GL_POINTS;
case DrawMode::LineStrip:
return GL_LINE_STRIP;
case DrawMode::LineLoop:
return GL_LINE_LOOP;
case DrawMode::Lines:
return GL_LINES;
case DrawMode::Triangles:
return GL_TRIANGLES;
case DrawMode::TriangleStrip:
return GL_TRIANGLE_STRIP;
case DrawMode::TriangleFan:
return GL_TRIANGLE_FAN;
}
std::cout << "DrawModeToGLEnum unreachable code hit\n";
return 0;
}
void Draw(unsigned int vertexCount, DrawMode mode)
{
glDrawArrays(DrawModeToGLEnum(mode), 0, vertexCount);
}
void DrawInstanced(unsigned int vertexCount, DrawMode mode, unsigned int numInstances)
{
glDrawArraysInstanced(DrawModeToGLEnum(mode), 0, vertexCount, numInstances);
}
void Draw(IndexBuffer& inIndexBuffer, DrawMode mode)
{
unsigned int handle = inIndexBuffer.GetHandle();
unsigned int numIndices = inIndexBuffer.Count();
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, handle);
glDrawElements(DrawModeToGLEnum(mode), numIndices, GL_UNSIGNED_INT, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
void DrawInstanced(IndexBuffer& inIndexBuffer, DrawMode mode, unsigned int instanceCount)
{
unsigned int handle = inIndexBuffer.GetHandle();
unsigned int numIndices = inIndexBuffer.Count();
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, handle);
glDrawElementsInstanced(DrawModeToGLEnum(mode), numIndices, GL_UNSIGNED_INT, 0, instanceCount);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
Working with textures
此书里面所有的漫反射光都不是计算出来的,而都是从贴图里读取出来的,这里的图片都是png 格式的,用stb_image 库来读取进来。stb是一个文件库的集合(Stb is a collection of single-file public domain libraries),代码在https://github.com/nothings/stb.
把对应的stb_image.h 头文件加入到自己的项目里,然后创建一个对应的cpp文件,加cpp文件应该是为了让它被编译,代码如下:
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
然后就可以创建自己的Texture类了:
#ifndef _H_TEXTURE_
#define _H_TEXTURE_
class Texture
{
protected:
unsigned int mWidth;
unsigned int mHeight;
unsigned int mChannels;
unsigned int mHandle;
private:
Texture(const Texture& other);
Texture& operator=(const Texture& other);
public:
Texture();
Texture(const char* path);
~Texture();
void Load(const char* path);
void Set(unsigned int uniformIndex, unsigned int textureIndex);
void UnSet(unsigned int textureIndex);
unsigned int GetHandle();
};
#endif
#include "Texture.h"
#include "stb_image.h"
#include "glad.h"
Texture::Texture()
{
mWidth = 0;
mHeight = 0;
mChannels = 0;
glGenTextures(1, &mHandle);
}
Texture::Texture(const char* path)
{
glGenTextures(1, &mHandle);
Load(path);
}
Texture::~Texture()
{
glDeleteTextures(1, &mHandle);
}
void Texture::Load(const char* path)
{
glBindTexture(GL_TEXTURE_2D, mHandle);
int width, height, channels;
unsigned char* data = stbi_load(path, &width, &height, &channels, 4);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
stbi_image_free(data);
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_NEAREST_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D, 0);
mWidth = width;
mHeight = height;
mChannels = channels;
}
void Texture::Set(unsigned int uniformIndex, unsigned int textureIndex)
{
glActiveTexture(GL_TEXTURE0 + textureIndex);
glBindTexture(GL_TEXTURE_2D, mHandle);
glUniform1i(uniformIndex, textureIndex);
}
void Texture::UnSet(unsigned int textureIndex)
{
glActiveTexture(GL_TEXTURE0 + textureIndex);
glBindTexture(GL_TEXTURE_2D, 0);
glActiveTexture(GL_TEXTURE0);
}
unsigned int Texture::GetHandle()
{
return mHandle;
}
添加具体的shaders文件
前面已经写好了Shader类了,以前学的OpenGL里的Shader是一个很多东西的类,这本书里把里面的Uniform、Texture类的东西都剥离出去了,这里补充vs和fs两个shader文件,就行了
#version 330 core
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
in vec3 position;
in vec3 normal;
in vec2 texCoord;
out vec3 norm;
out vec3 fragPos;
out vec2 uv;
void main()
{
gl_Position = projection * view * model * vec4(position, 1.0);
fragPos = vec3(model * vec4(position, 1.0));
norm = vec3(model * vec4(normal, 0.0f));
uv = texCoord;
}
#version 330 core
in vec3 norm;
in vec3 fragPos;
in vec2 uv;
uniform vec3 light;
uniform sampler2D tex0;
out vec4 FragColor;
void main()
{
vec4 diffuseColor = texture(tex0, uv);
vec3 n = normalize(norm);
vec3 l = normalize(light);
float diffuseIntensity = clamp(dot(n, l) + 0.1, 0, 1);
FragColor = diffuseColor * diffuseIntensity;
}
总结
这一章的内容本来没想单独写一篇文章的,但是代码量确实有点大。这篇文章里说到的,对渲染的抽象,在我看来,只是在头文件里没有用到任何OpenGL的API而已,说是abstraction layer实在是有点勉强… 总之,算是对OpenGL的复习和巩固吧
这一章的github仓库里额外提供了一个DebugDraw 类,可以帮助debug简单的图元,下一章会开始学习glTF这个文件格式。
|