Related
I've written this code to render a 2d map of square tiles:
#define TILE_NUM_INDICES 6
inline static u32 GetRandomIntBetween(u32 min, u32 max) {
return (u32)rand() % (max - min + 1) + min;
}
static void GetRandomTileMap(u32* map, u32 size) {
for (int i = 0; i < size; i++) {
u32 r = GetRandomIntBetween(0, 23);
map[i] = r;
}
}
NewRenderer::NewRenderer(const NewRendererInitialisationInfo& info)
:m_tileShader("shaders\\TilemapVert2.glsl", "shaders\\TilemapFrag2.glsl"),
m_worldMapSize(info.tilemapSizeX, info.tilemapSizeY),
m_tilemapChunkSize(info.chunkSizeX, info.chunkSizeY),
m_windowWidth(info.windowWidth),
m_windowHeight(info.windowHeight)
{
using namespace std;
const u32 mapsize = info.tilemapSizeX * info.tilemapSizeY;
m_worldTextureBytes = make_unique<u32[]>(mapsize);
GetRandomTileMap(m_worldTextureBytes.get(), mapsize);
glGenTextures(1, &m_worldTextureHandle);
glBindTexture(GL_TEXTURE_2D, m_worldTextureHandle);
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); // GL_NEAREST is the better filtering option for this game
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, info.tilemapSizeX, info.tilemapSizeY, 0, GL_RED, GL_UNSIGNED_INT, m_worldTextureBytes.get());
glGenerateMipmap(GL_TEXTURE_2D);
glGenVertexArrays(1, &m_vao);
}
void NewRenderer::DrawChunk(
const glm::ivec2& chunkWorldMapOffset,
const glm::vec2& pos,
const glm::vec2& scale,
float rotation,
ArrayTexture2DHandle texArray,
const Camera2D& cam
) const
{
m_tileShader.use();
glm::mat4 model = glm::mat4(1.0f);
model = glm::translate(model, glm::vec3(pos, 0.0f));
model = glm::rotate(model, glm::radians(rotation), glm::vec3(0.0f, 0.0f, 1.0f));
model = glm::scale(model, glm::vec3(scale, 1.0f));
m_tileShader.setMat4("vpMatrix", cam.GetProjectionMatrix(m_windowWidth, m_windowHeight));
m_tileShader.setMat4("modelMatrix", model);
m_tileShader.SetIVec2("chunkOffset", chunkWorldMapOffset);
m_tileShader.SetIVec2("chunkSize", m_tilemapChunkSize);
m_tileShader.setInt("masterTileTexture", 0);
m_tileShader.setInt("atlasSampler", 1);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, m_worldTextureHandle);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D_ARRAY, texArray);
glBindVertexArray(m_vao);
glDrawArrays(GL_TRIANGLES, 0, m_tilemapChunkSize.x * m_tilemapChunkSize.y * TILE_NUM_INDICES);
}
(Vertex shader)
#version 440 core
/*
cpp setup:
create a big index buffer
*/
layout (location = 0) in vec2 pos;
layout (location = 1) in vec2 uv;
out vec3 TexCoords;
uniform mat4 vpMatrix;
uniform mat4 modelMatrix;
uniform ivec2 chunkOffset;
uniform ivec2 chunkSize;
uniform sampler2D masterTileTexture;
#define TILE_NUM_VERTS 4
#define NUM_TILE_INDICES 6
void main()
{
// vertices and indices that make up two triangles (a quad)
// ie one tile in the map
vec4 vertices[TILE_NUM_VERTS] = vec4[TILE_NUM_VERTS](
vec4(0.5f, 0.5f, 1.0f, 1.0f),
vec4(0.5f, -0.5f, 1.0f, 0.0f),
vec4(-0.5f, -0.5f, 0.0f, 0.0f),
vec4(-0.5f, 0.5f, 0.0f, 1.0f)
);
int indices[NUM_TILE_INDICES] = int[NUM_TILE_INDICES](
0, 1, 3, // first triangle
1, 2, 3 // second triangle
);
// cycle through indicies
int index = indices[int(gl_VertexID % NUM_TILE_INDICES)];
// get base vertex
vec4 baseVertex = vertices[index];
// which tile in the map is being drawn?
int whichTile = gl_VertexID / NUM_TILE_INDICES;
// transfrom into x y coords of tile in the chunk
ivec2 tilexy = ivec2(int(whichTile / chunkSize.y), int(whichTile % chunkSize.y));
// translate base vertex by tilexy
baseVertex.xy += vec2(tilexy);
// set the z coord of the tex coords passed based on what tile is here
// in the master tile map.
// based on shader output all steps up to here are successful, a grid is drawn.
// The problem is the texelFetch is not working, it's always the same tile drawn.
TexCoords = vec3(
baseVertex.zw,
// changing this to different hard coded values does change what tile is drawn as expectd so sampler2DArray is setup correctly
float(texelFetch(masterTileTexture, tilexy + chunkOffset, 0).r));
gl_Position = vpMatrix * modelMatrix * vec4(baseVertex.xy, 0.0, 1.0);
}
(Frag shader)
#version 440 core
uniform sampler2DArray atlasSampler;
in vec3 TexCoords;
out vec4 FragColor;
void main()
{
FragColor = texture(atlasSampler, TexCoords);
}
The idea is that it will be used to draw chunks of a large texture, each pixel of which represents a tile. The basic premise seems to work, a grid of tiles is drawn, however the texelFetch line in the vertex shader does not seem to be working, or the texture containing the tile indices is not set up properly as it is only ever the tile with index 0 that is drawn.
To test it I've tried to make a texture which contains random values for the tile index texture, debugging the code I can see that random values are inserted into the texture buffer.
I've used texelFetch before in a shader and it's worked and as far as I can tell I am using it right.
Can anyone spot what is wrong with my code?
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, info.tilemapSizeX, info.tilemapSizeY, 0, GL_RED, GL_UNSIGNED_INT, m_worldTextureBytes.get());
This creates a texture in a normalized fixed-point format. When you read it in the shader (through texelFetch) the value is always going to be between 0 and 1, thus sampling the 0th layer from the array texture.
OpenGL 4.4 supports integer texture formats, which is what you should use here. Replace the first GL_RED with GL_R8UI, GL_16UI or GL_R32UI, whichever is more appropriate, and the second GL_RED with GL_RED_INTEGER. E.g.:
glTexImage2D(GL_TEXTURE_2D, 0, GL_R32UI, //<---
info.tilemapSizeX, info.tilemapSizeY, 0, GL_RED_INTEGER, //<---
GL_UNSIGNED_INT, m_worldTextureBytes.get());
Additionally you have to change the sampler2D in the shader to a matching integer sampler type. For the above internal format, the matching sampler would be usampler2D:
uniform usampler2D masterTileTexture;
EDIT: Also you have to set the MAG filter to GL_NEAREST, since it's the only one that's supported:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
(MIN filter could also be GL_NEAREST_MIPMAP_NEAREST.)
I am working on sending multiple textures to a single shader and am having a weird issue where both samplers in the shader seem to get the same texture data. I know there are a lot of other multiple texture questions and answers out there (Here are a few I've read multiple times already 1, 2, 3) but some bug is eluding me and I'm starting to lose my marbles. I am fairly confident I have everything setup correctly but obviously there is still some issue.
So, currently I have Shape, Material, Texture, and Shader classes. My shape class is the parent that performs the actual draw. It has a material member which has a shader and an array of textures. The material class draw looks like this:
void Shape::Draw(GLenum mode, glm::mat4& model, glm::mat4& view, glm::mat4& proj)
{
m_Material.Enable();
m_Material.UpdateTransform(model, view, proj);
glBindVertexArray(m_VAO);
glDrawElements(mode, m_NumVerts, GL_UNSIGNED_INT, 0);
m_Material.Disable();
}
Here is my whole material class:
#include "pch.h"
#include "Material.h"
Material::Material() :
m_LightService(LightService::GetInstance())
{
OGLR_CORE_INFO("CREATING MATERIAL");
}
void Material::SetShader(std::string fileName)
{
m_Shader.SetShaderFileName(fileName);
}
void Material::Enable() {
m_Shader.Bind();
for (const auto text : m_Textures) {
text->Enable();
}
UploadUniforms();
}
void Material::Disable() {
m_Shader.Unbind();
for (const auto text : m_Textures) {
text->Disable();
}
}
void Material::AddTexture(std::string fileName, std::string typeName) {
m_Textures.push_back(std::make_shared<Texture>(fileName, m_Shader.ShaderId(), typeName, m_Textures.size()));
}
void Material::UpdateTransform(glm::mat4& model, glm::mat4& view, glm::mat4& proj) {
m_Shader.UploadUniformMat4("u_Projection", proj);
m_Shader.UploadUniformMat4("u_View", view);
m_Shader.UploadUniformMat4("u_Model", model);
}
void Material::UploadUniforms() {
if (m_Shader.isLoaded()) {
auto ambient = m_LightService->GetAmbientLight();
m_Shader.UploadUniformFloat3("uAmbientLight", ambient.strength * ambient.color);
}
}
void Material::SetMaterialData(std::shared_ptr<MaterialData> matData) {
AddTexture(matData->ambient_texname, "t_Ambient"); // Wall
AddTexture(matData->diffuse_texname, "t_Diffuse"); // Farm
}
As you can see, when the material receives the material data from the .mtl file of the .obj we are rendering in the Material::SetMaterialData function, we are adding two new texture objects to the list of textures. We are passing in the filename to be loaded and the string identifier of the glsl uniform sampler.
When the material is enabled, we are enabling the shader and each of the texture objects.
Here is the wip of my Texture class.
#include "pch.h"
#include "Texture.h"
#include <stb_image.h>
Texture::Texture(std::string fileName, uint32_t programId, std::string uniformId, uint16_t unitId)
{
m_FileName = ASSET_FOLDER + fileName;
unsigned char* texData = stbi_load(m_FileName.c_str(), &m_Width, &m_Height, &m_NrChannels, 0);
m_ProgramId = programId;
glUniform1i(glGetUniformLocation(programId, uniformId.c_str()), unitId);
glGenTextures(1, &m_TextureId);
m_TextureUnit = GL_TEXTURE0 + unitId;
glActiveTexture(m_TextureUnit);
glBindTexture(GL_TEXTURE_2D, m_TextureId);
if (texData)
{
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, m_Width, m_Height, 0, GL_RGB, GL_UNSIGNED_BYTE, texData);
glGenerateMipmap(GL_TEXTURE_2D);
}
else
{
OGLR_CORE_ERROR("Failed to load texture");
throw std::runtime_error("Failed to load texture: "+ m_FileName);
}
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);
stbi_image_free(texData);
Disable();
}
void Texture::Enable()
{
glActiveTexture(m_TextureUnit); // activate the texture unit first before binding texture
glBindTexture(GL_TEXTURE_2D, m_TextureId);
}
void Texture::Disable()
{
glBindTexture(GL_TEXTURE_2D, 0);
}
So, the first thing I do is grab the ID of the sampler uniform from the shader and bind that sample to the texture unit I'm looking for. We then generate that texture, activate the same unit and bind my generated texture to it. I'm guessing that it is somewhere in here that I have blundered but I can't seem to figure out how.
Here are my shaders as they currently stand.
// vertex
#version 330 core
layout (location = 0) in vec3 a_Position;
layout (location = 1) in vec3 aNormal;
layout (location = 2) in vec2 aTexCoord;
out vec3 outNormal;
out vec2 TexCoord;
uniform mat4 u_Projection;
uniform mat4 u_View;
uniform mat4 u_Model;
void main() {
vec4 worldPosition = u_Model * vec4(a_Position,1.0);
gl_Position = u_Projection * u_View * worldPosition;
outNormal = aNormal;
TexCoord = aTexCoord;
}
// fragment
#version 330 core
out vec4 color;
in vec3 outNormal;
in vec2 TexCoord;
uniform sampler2D t_Ambient;
uniform sampler2D t_Diffuse;
void main() {
if (TexCoord.x > 0.50)
{
//color = vec4(TexCoord.x, TexCoord.y, 0.0, 1.0);
color = texture(t_Diffuse, TexCoord);
}
else
{
color = texture(t_Ambient, TexCoord);
}
}
I am expecting each half of my triangle to have different textures but for some reason both samplers seem to get the same texture. If I use that color in the frag shader instead of the texture I get half texture and half the color so it... that at least works...
The other thing that I noticed that I thought was weird was that the texture that gets rendered seems to always be the first one I add. If I flip the order of the AddTexture calls in Material::SetMaterialData the other texture appears. Maybe someone can explain to my why that would be obvious but I would have expected that if I had somehow goofed the binding of my textures that it would be the second one overwriting the first but hey ¯_(ツ)_/¯ I'm ready to be educated on that one.
Edit
I apologize but apparently it was not clear that the shader is being properly bound.
At the beginning of the Shape::Draw function we are calling m_Material.Enable();
The beginning of which calls m_Shader.Bind(); which in turn calls glUseProgram(m_ProgramId);
This occurs before any of the texture creation flow so the shader is properly bound before we are setting the uniforms.
Apologies for any confusion.
glUniform1i binds an uniform only for the currently enabled shader:
glUniform operates on the program object that was made part of current state by calling glUseProgram.
Seems like you don't call glUseProgram before glUniform1i(glGetUniformLocation(programId, uniformId.c_str()), unitId); (I can't say for sure without caller code of the SetMaterialData) and the uniform is not actually binded to the unitId for the shader.
So try this:
glUseProgram(programId);
glUniform1i(glGetUniformLocation(programId, uniformId.c_str()), unitId);
glGenTextures(1, &m_TextureId);
First time trying to implement shadow map using openGL ang glsl shader language.
I think the first pass where I render to a texture is correct but when I compare the depth values it seems to shadow everything.
https://www.dropbox.com/s/myxenx9y41yz2fc/Screenshot%202014-12-09%2012.18.53.png?dl=0
My perspective projection matrix looks like this:
FOV = 90
Aspect = According to the programs window size. (I also tried to put different values here)
Near = 2;
Far= 10000;
Function to initialize the frame buffer
void OpenGLWin::initDepthMap()
{
//Framebuffer
m_glFunctions->glGenFramebuffers(1, &m_frameBuffer);
m_glFunctions->glBindFramebuffer(GL_FRAMEBUFFER, m_frameBuffer);
//////////////////////////////////////////////////////////////////////////
//Texture to render scene to
m_glFunctions->glGenTextures(1, &m_renderToTexture);
//Bind created texture to make it current
m_glFunctions->glBindTexture(GL_TEXTURE_2D, m_renderToTexture);
//Creates an empty texture of specified size.
//m_glFunctions->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1024, 768, 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
m_glFunctions->glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, 1024, 1024, 0, GL_DEPTH_COMPONENT, GL_FLOAT, 0);
m_glFunctions->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
m_glFunctions->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
m_glFunctions->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
m_glFunctions->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
m_glFunctions->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
m_glFunctions->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE);
m_glFunctions->glDrawBuffer(GL_NONE);
m_glFunctions->glReadBuffer(GL_NONE);
// Always check that our framebuffer is ok
if (m_glFunctions->glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE){
qDebug() << "FrameBuffer not OK";
return;
}
m_glFunctions->glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
Draw function for each mesh. Model matrix is passed as argument from a Transform class draw function
void Mesh::draw(const Matrix4x4& projection, const Matrix4x4& view, const Matrix4x4& model)
{
//Shadow map pass 1
if (m_shadowMapFirstpass){
//Pass 1 Shaders
m_glFunctions->glUseProgram(m_depthRTTShaderProgram);
//Light view matrix
m_depthMVP = projection*view*model;
//Get the location of the uniform name mvp
GLuint depthMVPLocation = m_glFunctions->glGetUniformLocation(m_depthRTTShaderProgram, "depthMVP");
m_glFunctions->glUniformMatrix4fv(depthMVPLocation, 1, GL_TRUE, &m_depthMVP[0][0]);
m_shadowMapFirstpass = false;
}
//Shadow map pass 2
else if(m_shadowMapFirstpass == false){
//Pass 2 Shader
m_glFunctions->glUseProgram(m_shaderProgram);
//Gets the model matrix which is then multiplied with view and projection to form the mvp matrix
Matrix4x4 mvp = projection * view * model;
//Get the location of the uniform name mvp
GLuint mvpLocation = m_glFunctions->glGetUniformLocation(m_shaderProgram, "mvp");
//Send the mvp matrix to the vertex shader
m_glFunctions->glUniformMatrix4fv(mvpLocation, 1, GL_TRUE, &mvp[0][0]);
Matrix4x4 depthBiasMVP = m_depthMVP;// biasMatrix*m_depthMVP;
GLuint depthBiasMVPLocation = m_glFunctions->glGetUniformLocation(m_shaderProgram, "depthBiasMVP");
m_glFunctions->glUniformMatrix4fv(depthBiasMVPLocation, 1, GL_TRUE, &depthBiasMVP[0][0]);
m_shadowMapFirstpass = true;
}
//Bind this mesh VAO
m_glFunctions->glBindVertexArray(m_vao);
//Draw the triangles using the index buffer(EBO)
glDrawElements(GL_TRIANGLES, m_indices.size(), GL_UNSIGNED_INT, 0);
//Unbind the VAO
m_glFunctions->glBindVertexArray(0);
/////////////////////////////////////////////////////////////////////////////////////////////////////
//Calls the childrens' update
if (!m_children.empty())
{
for (int i = 0; i < m_children.size(); i++)
{
if (m_children[i] != NULL)
{
m_children[i]->draw(frustumCheck, projection, view, bvScaleFactor, model);
}
}
}
}
My render loop
void OpenGLWin::paintGL()
{
// m_glFunctions->glBindFramebuffer(GL_FRAMEBUFFER, m_frameBuffer);
m_glFunctions->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_frameBuffer);
glViewport(0, 0, 1024, 1024);
// Clear the buffer with the current clearing color
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//Light View Matrix
Matrix4x4 lightView;
lightView.lookAt(Vector3(0, 0, 0), Vector3(0, 0, -1), Vector3(0, 1, 0));
//Draw scene to Texture
m_root->draw(m_projection, lightView);
///////////////////////////////////////////////////////////////////
//Draw to real scene
m_glFunctions->glBindFramebuffer(GL_FRAMEBUFFER, 0);
// m_glFunctions->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
// Clear the screen
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//Bind Pass 2 shader
m_glFunctions->glUseProgram(m_shadowMapShaderProgram->getShaderProgramID());
GLuint shadowMapLocation = m_glFunctions->glGetUniformLocation(m_shadowMapShaderProgram->getShaderProgramID(), "shadowMap");
//Shadow Texture
m_glFunctions->glActiveTexture(GL_TEXTURE0);
m_glFunctions->glBindTexture(GL_TEXTURE_2D, m_renderToTexture);
m_glFunctions->glUniform1i(shadowMapLocation, 0);
//Updates matrices and view matrix for player camera
m_root->update(m_view);
//Render scene to main frame buffer
m_root->draw(m_projection, m_view);
}
Pass 1 Vertex Shader
#version 330 core
//Passthrough vertex shader
uniform mat4 depthMVP;
//Vertex received from the program
layout(location = 0) in vec3 vertexPosition_modelspace;
void main(void)
{
//Output position of vertex in clip space
gl_Position = depthMVP * vec4(vertexPosition_modelspace, 1);
}
Pass 1 Fragment Shader
#version 330 core
//Render to texture
// Ouput data
layout(location = 0) out float depthValue;
void main(void)
{
depthValue = gl_FragCoord.z;
}
Pass 2 Vertex Shader
#version 330 core
layout(location = 0) in vec3 vertexPosition_modelspace;
out vec4 ShadowCoord;
// Values that stay constant for the whole mesh.
uniform mat4 mvp;
uniform mat4 depthBiasMVP;
void main(){
// Output position of the vertex, in clip space : MVP * position
gl_Position = mvp * vec4(vertexPosition_modelspace,1);
ShadowCoord = depthBiasMVP * vec4(vertexPosition_modelspace,1);
}
Pass 2 Fragment Shader
#version 330 core
in vec4 ShadowCoord;
// Ouput data
layout(location = 0) out vec3 color;
// Values that stay constant for the whole mesh.
uniform sampler2D shadowMap;
void main(){
float visibility=1.0;
vec3 ProjCoords = ShadowCoord.xyz / ShadowCoord.w;
vec2 UVCoords;
UVCoords.x = 0.5 * ProjCoords.x + 0.5;
UVCoords.y = 0.5 * ProjCoords.y + 0.5;
float z = 0.5 * ProjCoords.z + 0.5;
float Depth = texture(shadowMap, UVCoords).z;//or x
if (Depth < (z + 0.00001)){
visibility = 0.1;
}
color = visibility*vec3(1,0,0);
}
Disable texture comparison for one thing. That's only valid when used with sampler2DShadow and you clearly are not using that in your code because your texture coordinates are 2D.
This means replacing the following code:
m_glFunctions->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
m_glFunctions->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE);
With this instead:
m_glFunctions->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE);
Likewise, using GL_LINEAR filtering on a non-sampler2DShadow texture is a bad idea. That is going to average the 4 nearest depth values and give you a single depth back. But that's not the proper way to anti-alias shadows; you actually want to average the result of 4 depth tests instead of doing a single test on the average of 4 depths.
I try to render my geometry in the first pass and render it from a buffer to a quad in my second pass.
Rendering the geometry directly to screen works, but as soon as I try to render to the quad with multiple passes, the screen stays black (the clear-color is grey).
I think my shader might be the problem. Please just ask for more code if needed.
Vertex Shader:
#version 440
layout(location = 0) in vec3 vertexPosition;
layout(location = 1) in vec2 vertexUV;
layout(location = 2) in vec3 vertexNormal;
centroid out vec2 UV;
out vec4 position;
out vec3 normal;
uniform mat4 uniMatModel;
uniform mat4 uniMatView;
uniform mat4 uniMatProjection;
uniform mat4 uniMatModelView;
uniform mat4 uniMatModelViewProjection;
uniform mat3 uniMatNormal;
void main()
{
UV = vertexUV;
position = uniMatModelView * vec4(vertexPosition, 1.0);
normal = normalize(uniMatNormal * vertexNormal);
gl_Position = uniMatModelViewProjection * vec4(vertexPosition, 1.0);
}
Fragment Shader:
#version 440
centroid in vec2 UV;
in vec4 position;
in vec3 normal;
layout (location = 0) out vec4 fragColor;
layout (location = 1) out vec3 positionData;
layout (location = 2) out vec3 normalData;
layout (location = 3) out vec3 colorData;
subroutine void renderPassType();
subroutine uniform renderPassType renderPass;
uniform sampler2D uniSamTexture;
uniform sampler2D uniSamAlpha;
layout(binding = 0) uniform sampler2D positionTex;
layout(binding = 1) uniform sampler2D normalTex;
layout(binding = 2) uniform sampler2D colorTex;
subroutine (renderPassType)
void renderWorld()
{
if (texture2D(uniSamAlpha, UV).rgb[0] == 1.0) // Alphamaps have to be inverted!
{
discard;
colorData = vec3(0.0);
}
else
{
colorData = texture2D(uniSamTexture, UV).rgb;
}
positionData = vec3(position.x, position.y, position.z);
normalData = normal;
}
subroutine (renderPassType)
void renderLight()
{
fragColor = vec4(texture(colorTex, UV).rgb, 1.0);
}
void main()
{
renderPass();
}
Edit:
void C0::Cocaine::makeFBO(GLuint &fbo)
{
GLuint depthBuf;
Texture
posTex(this),
normTex(this),
colorTex(this)
;
// Create and bind the FBO
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
// The depth buffer
glGenRenderbuffers(1, &depthBuf);
glBindRenderbuffer(GL_RENDERBUFFER, depthBuf);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, static_cast<GLsizei>(resolution.x), static_cast<GLsizei>(resolution.y));
// Create the textures for position, normal and color
posTex.createGBuf(GL_TEXTURE0, GL_RGB32F, static_cast<int>(resolution.x), static_cast<int>(resolution.y));
normTex.createGBuf(GL_TEXTURE1, GL_RGB32F, static_cast<int>(resolution.x), static_cast<int>(resolution.y));
colorTex.createGBuf(GL_TEXTURE2, GL_RGB8, static_cast<int>(resolution.x), static_cast<int>(resolution.y));
// Attach the textures to the framebuffer
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthBuf);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, posTex.getTextureID(), 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, normTex.getTextureID(), 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D, colorTex.getTextureID(), 0);
GLenum drawBuffers[] = { GL_NONE, GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2 };
glDrawBuffers(4, drawBuffers);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
void C0::Texture::createGBuf(GLenum texUnit, GLenum format, int width, int height)
{
this->width = width;
this->height = height;
glActiveTexture(texUnit);
glGenTextures(1, &textureID);
glBindTexture(GL_TEXTURE_2D, textureID);
glTexStorage2D(GL_TEXTURE_2D, 1, format, width, height);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
}
Edit: The render method:
void C0::Mesh::render()
{
if (shader != nullptr)
{
shader->use();
shader->setModelMatrix(modelMatrix);
}
glBindVertexArray(VAO);
if (!is2D)
{
for (int i = 0x1; i <= 0xB; i++)
{
if (texture[i] != nullptr)
{
glBindMultiTextureEXT(GL_TEXTURE0 + i, GL_TEXTURE_2D, texture[i]->getTextureID());
}
}
}
glDrawArrays(GL_TRIANGLES, 0, indexCount);
glBindVertexArray(0);
}
More code:
pass1->setRenderCallback([&]() {
sp->useSubRoutine(srp1);
cube->render();
return true;
});
pass2->setRenderCallback([&]() {
sp->useSubRoutine(srp2);
sp->resetMatrices(); // it should be a 2D quad, so I reset the MVP uniforms to 1.0f
sp->lockMVP(true); // workaround (still playing around with everything)
dq->render(); // render display quad
sp->lockMVP(false);
return true;
});
e->setOnRender([&]() {
pass1->render();
pass2->render();
/* handle input etc... */
});
Pass render method:
bool Pass::render()
{
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
if (depthTest)
{
glEnable(GL_DEPTH_TEST);
}
else
{
glDisable(GL_DEPTH_TEST);
}
if (onRender != nullptr)
{
if (!onRender())
{
return false;
}
}
if (fbo != 0)
{
glFinish();
}
return true;
}
With the shader version string you use:
#version 440
the default for the profile is core. From the GLSL 4.40 spec:
A profile argument can only be used with version 150 or greater. If no profile argument is provided and the version is 150 or greater, the default is core.
The texture2D() function you are calling in the fragment shader is only available in the compatibility profile. In the core profile, you need to use the overloaded texture():
if (texture(uniSamAlpha, UV).rgb[0] == 1.0) // Alphamaps have to be inverted!
...
colorData = texture(uniSamTexture, UV).rgb;
If you have problems with your shaders, it's always a good idea to check that the compilation was successful. You can use the following calls for this:
glGetShaderiv(shaderId, GL_COMPILE_STATUS, ...);
glGetShaderInfoLog(shaderId, ...);
glDrawBuffers has a GL_NONE even though you are using a renderbuffer for depth.
Remove it.
My program has been working perfectly so far, but it turns out that I've been lucky. I began doing some cleanup of the shader, because it was full of experimental stuff, and I had the following line at the end of the fragment shader:
gl_FragColor = final_color * (texture2D(tex, gl_TexCoord[0].st)*1.0 + texture2D(tex2, gl_TexCoord[0].st)*1.0);
I attempted to clean it up and I had the following declared at the top:
uniform sampler2D tex, tex2;
Changing these lines to:
gl_FragColor = final_color * texture2D(tex, gl_TexCoord[0].st;
and
uniform sampler2D tex;
actually broke the program (black screen), even though I am doing
GLuint tex_loc = glGetUniformLocation(shader_prog_id_, "tex");
glUniform1i(tex_loc, texture_id_);
in my main code. I'm sure it's a texture issue and not a glsl compiler error, because I can add 1.0 to the output and end up with a white silhouette of my mesh.
The strangeness begins when I change the lines in my shader to:
gl_FragColor = final_color * texture2D(tex2, gl_TexCoord[0].st;
and
uniform sampler2D tex2;
but still retrieve the location for tex. The program works as it always has, even though inspecting the value of tex_loc in the debugger indicates an error. I'm not happy doing this, and now that I'm trying to load multiple textures, it will cause bigger headaches down the line.
I'm using VBOs, in interleaved format, to render the geometry. I'm passing in the vertex position, normal and texcoord this way.
There are other questions with the "black texture" issue, but they're using immediate mode calls and setting the wrong texture state. I tried changing the texture unit before supplying the arrays, with no success.
Here is as much relevant code as possible from the main program:
void MeshWidget::draw() {
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0.0f, 0.0f, -4.0f + zoom_factor_);
glRotatef(rotX, 1.0f, 0.0f, 0.0f);
glRotatef(rotY, 0.0f, 1.0f, 0.0f);
glRotatef(rotZ, 0.0f, 0.0f, 1.0f);
// Auto centre mesh based on vertex bounds.
glTranslatef(-x_mid_, -y_mid_, -z_mid_);
glDrawElements(GL_TRIANGLES, mesh_.num_indices, GL_UNSIGNED_SHORT, BUFFER_OFFSET(0)); //The starting point of the IBO
}
void MeshWidget::openMesh(const string& filename) {
if (mesh_filename_ != filename) {
clearMeshData(mesh_);
glDeleteBuffersARB(1, &VertexVBOID);
glDeleteBuffersARB(1, &IndexVBOID);
ReadMsh(mesh_, filename);
// Create buffer objects here.
glGenBuffersARB(1, &VertexVBOID);
glBindBufferARB(GL_ARRAY_BUFFER, VertexVBOID);
glBufferDataARB(GL_ARRAY_BUFFER, sizeof(VertexAttributes)*mesh_.num_vertices, &mesh_.vertices[0], GL_STATIC_DRAW);
glGenBuffersARB(1, &IndexVBOID);
glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER, IndexVBOID);
glBufferDataARB(GL_ELEMENT_ARRAY_BUFFER, sizeof(uint16_t)*mesh_.num_indices, &mesh_.indices[0], GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, VertexVBOID);
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_FLOAT, sizeof(VertexAttributes), BUFFER_OFFSET(0)); //The starting point of the VBO, for the vertices
glEnableClientState(GL_NORMAL_ARRAY);
glNormalPointer(GL_FLOAT, sizeof(VertexAttributes), BUFFER_OFFSET(12)); //The starting point of normals, 12 bytes away
glClientActiveTexture(GL_TEXTURE0);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glTexCoordPointer(2, GL_FLOAT, sizeof(VertexAttributes), BUFFER_OFFSET(24)); //The starting point of texcoords, 24 bytes away
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IndexVBOID);
if (startup_done_) updateGL();
}
}
void MeshWidget::openTexture(const string& filename) {
size_t dot = filename.find_last_of('.');
string ext(filename, dot, filename.size()); // 3rd parameter should be length of new string, but is internally clipped to end.
glActiveTexture(GL_TEXTURE0);
glClientActiveTexture(GL_TEXTURE0);
glDeleteTextures(1, &texture_id_);
glGenTextures(1, &texture_id_);
glBindTexture(GL_TEXTURE_2D, texture_id_);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
if (ext == ".dds") {
if (GLEE_EXT_texture_compression_s3tc) {
texture_id_ = SOIL_load_OGL_texture(filename.c_str(), SOIL_LOAD_AUTO, texture_id_, SOIL_FLAG_DDS_LOAD_DIRECT);
// SOIL takes care of calling glTexParams, glTexImage2D, etc.
yflip_texture_ = true;
} else {
//std::cout << "S3TC not supported on this graphics hardware." << std::endl;
// TODO: Error message in status bar?
}
} else {
QImage tex(filename.c_str());
tex = QGLWidget::convertToGLFormat(tex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex.width(), tex.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, tex.bits());
yflip_texture_ = false;
}
updateUniforms();
if (startup_done_) updateGL();
}
void MeshWidget::updateUniforms() {
GLuint texture_flip_uniform = glGetUniformLocation(shader_prog_id_, "yflip");
glUniform1f(texture_flip_uniform, float(yflip_texture_ * 1.0f));
GLuint tex_loc = glGetUniformLocation(shader_prog_id_, "tex");
glUniform1i(tex_loc, texture_id_);
}
And my shaders (there's still some junk in here because I was experimenting, but nothing that affects the output):
varying vec3 normal, lightDir, eyeVec;
uniform float yflip;
void main()
{
normal = gl_NormalMatrix * gl_Normal;
vec3 vVertex = vec3(gl_ModelViewMatrix * gl_Vertex);
lightDir = vec3(gl_LightSource[0].position.xyz - vVertex);
eyeVec = -vVertex;
gl_TexCoord[0].x = gl_MultiTexCoord0.x;
gl_TexCoord[1].x = gl_MultiTexCoord1.x;
if (yflip == 1.0) {
gl_TexCoord[0].y = 1 - gl_MultiTexCoord0.y;
gl_TexCoord[1].y = 1 - gl_MultiTexCoord1.y;
} else {
gl_TexCoord[0].y = gl_MultiTexCoord0.y;
gl_TexCoord[1].y = gl_MultiTexCoord1.y;
}
gl_Position = ftransform();
}
fragment shader:
varying vec3 normal, lightDir, eyeVec;
uniform sampler2D tex2;
void main (void)
{
vec4 texel = texture2D(tex2, gl_TexCoord[0].st);
vec4 final_color =
(gl_FrontLightModelProduct.sceneColor * gl_FrontMaterial.ambient) +
(gl_LightSource[0].ambient * gl_FrontMaterial.ambient);
vec3 N = normalize(normal);
vec3 L = normalize(lightDir);
float lambertTerm = dot(N,L);
if(lambertTerm > 0.0)
{
final_color += gl_LightSource[0].diffuse *
gl_FrontMaterial.diffuse *
lambertTerm;
vec3 E = normalize(eyeVec);
vec3 R = reflect(-L, N);
float specular = pow( max(dot(R, E), 0.0),
gl_FrontMaterial.shininess );
final_color += gl_LightSource[0].specular *
gl_FrontMaterial.specular *
specular;
}
gl_FragColor = final_color * texel;
}
glUniform1i(tex_loc, texture_id_);
The second parameter should specify ID of a texture unit(hint: glActiveTexture sets the currently active texture unit), not ID of particular texture object.