Custom shaders/material not working in a custom QQuickItem object - c++

I have tried following the tutorial given by QT's online documentation (QtQuick Scenegraph CustomMaterial Example), but when I debugged my program, the object does not show, appearing to be transparent. However, when I tried replacing my custom shader class with QSGFlatColorMaterial, and set a color, the object does show without any issues. I double checked my implementation of the tutorial with the example code in the example git repo.
Here is my code in question
/* MapDisplay Class */
MapDisplay::MapDisplay(QQuickItem* parent) : QQuickItem(parent) {
this->setFlag(ItemHasContents, true);
};
QSGNode* MapDisplay::updatePaintNode(QSGNode* old, UpdatePaintNodeData*) {
auto* node = static_cast<MapNode*>(old);
if (!node) {
node = new MapNode();
}
if (this->flag_geo_changed) {
node->ChangeRectBounds(this->boundingRect());
this->flag_geo_changed = false;
}
const QRectF rect = this->boundingRect();
auto* vertices = node->geometry()->vertexDataAsPoint2D();
vertices[0].set(rect.bottomLeft().x(), 1);
vertices[1].set(200, 0);
vertices[2].set(0, 200);
vertices[3].set(200, 200);
node->markDirty(QSGNode::DirtyGeometry);
return node;
}
void MapDisplay::geometryChange(const QRectF& new_geo, const QRectF& old_geo) {
this->flag_geo_changed = false;
this->update();
QQuickItem::geometryChange(new_geo, old_geo);
}
/* MapShader Class */
MapShader::MapShader() {
this->setShaderFileName(VertexStage, ":/geo/shader/map.vert.qsb");
this->setShaderFileName(FragmentStage, ":/geo/shader/map.frag.qsb");
};
/* MapMaterial Class */
MapMaterial::MapMaterial(){};
MapMaterial::~MapMaterial(){};
QSGMaterialType* MapMaterial::type() const {
static QSGMaterialType type;
return &type;
}
int MapMaterial::compare(const QSGMaterial* o) const {
Q_ASSERT(o && this->type() == o->type());
const auto* other = static_cast<const MapMaterial*>(o);
return other == this ? 0 : 1;
}
QSGMaterialShader* MapMaterial::createShader(
QSGRendererInterface::RenderMode) const {
return new MapShader();
}
/* MapNode Class */
MapNode::MapNode() {
// Material
auto* mat = new MapMaterial();
this->setMaterial(mat);
this->setFlag(QSGGeometryNode::OwnsMaterial, true);
// Geometry
auto* geo = get_geo_data::GetRectShape();
this->setGeometry(geo);
this->setFlag(QSGGeometryNode::OwnsGeometry, true);
}
void MapNode::ChangeRectBounds(const QRectF& bounds) {
QSGGeometry::updateTexturedRectGeometry(this->geometry(), bounds,
QRectF(0, 0, 0, 0));
this->markDirty(QSGNode::DirtyGeometry);
}
And here is the link to the example code I cross checked with customitem.cpp
Here are also my shaders too,
#version 460
// map.vert
layout(location = 0) in vec4 vertex_object;
layout(location = 1) in vec2 atex_coord;
layout(location = 0) out vec2 vtex_coord;
void main() {
gl_Position = vertex_object;
vtex_coord = atex_coord;
}
#version 460
// map.frag
layout(location = 0) out vec4 frag_color;
void main() {
frag_color = vec4(0.0, 1.0, 0.0, 1.0);
}
And here are also some screenshots too:
If I use QSGFlatColorMaterial
If I use my custom material and shaders

Ok, so after prodding through the example repo, I did not multiply the vertex_object in the vert shader with the matrix of the object.
So after implementing the methods and glsl code from the documention into mine's, I got the shader to show properly. Turns out that I did not properly set the shader position
map.cpp
bool MapShader::updateUniformData(RenderState& state, QSGMaterial* new_material,
QSGMaterial* old_material) {
bool changed = false;
QByteArray* buf = state.uniformData();
Q_ASSERT(buf->size() >= 64);
if (state.isMatrixDirty()) {
const QMatrix4x4 m = state.combinedMatrix();
std::memcpy(buf->data(), m.constData(), 64);
changed = true;
}
auto* cus_masterial = static_cast<MapMaterial*>(new_material);
if (old_material != new_material || cus_masterial->uniform.dirty) {
cus_masterial->uniform.dirty = false;
changed = true;
}
return changed;
}
map.vert
#version 460
layout(location = 0) in vec4 vertex_object;
layout(location = 1) in vec2 atex_coord;
layout(location = 0) out vec2 vtex_coord;
layout(std140, binding=0) uniform buf {
mat4 qt_matrix; // offset 0
} ubuf;
void main() {
gl_Position = ubuf.qt_matrix /* Very important */ * vertex_object;
vtex_coord = atex_coord;
}

Related

GLSL black screen, shader doesn't do anything

I am writing a basic Sphere-Tracer in a fragment shader, everything is doing fine if I just color points according to their surface normals, but as soon as I try to implement the reflecting algorithm, it takes a bit longer to compile, and just blackscreens. The fps starts out at 1 but quickly goes up to 1000, which is the code limit I put, telling me the shader is actually not doing anything and just ignores my code, which should be much slower than that. I initially thought I was hitting the instruction limit of my GPU, but I don't think that's the problem, as using the normal-shading code, I can set MAX_MARCH to high values, like 1000, but using reflections, even with MAX_REFLECTIONS and MAX_AA to one, which should be similar to the amount of instructions of normal-shading(maybe ~50 more, not significant I don't think). But I need to set MAX_MARCH to 1 for it to render, even setting it to two causes the bug.
Vertex shader:
//Draw a quad on the whole display and calculates sky color
#version 400 core
in vec3 position;
out vec2 uvPos;
out vec4 backColor;
void main(void){
gl_Position = vec4(position, 1.0);
uvPos = position.xy*0.5+0.5;
backColor = mix(vec4(1, 1, 1, 1), vec4(0.5, 0.7, 1, 1), uvPos.y);
}
Fragment shader:
#version 400 core
#define FLT_MAX 3.402823466e+38
#define FLT_MIN 1.175494351e-38
#define DBL_MAX 1.7976931348623158e+308
#define DBL_MIN 2.2250738585072014e-308
#define PI 3.141592653589793115997963468544185161590576171875
#define MAX_AA 1
#define MAX_MARCH 1000
#define MAX_REFLECTIONS 10
#define MAX_DIST 10
in vec2 uvPos;
in vec4 backColor;
out vec4 outColor;
int randomIterator = 0;
//############################################################ Structure definitions #########################################################################
struct Material{
int type;
vec3 albedo;
};
struct Object{
int type; //1:Sphere, 2:Box
vec3 center;
float radius;
vec3 size;
Material material;
};
struct Scene{
Object objects[3];
};
struct Ray{
vec3 origin;
vec3 dir;
};
struct HitRecord{
vec3 p;
vec3 n;
Object o;
Material mat;
float closest;
};
struct Camera{
vec3 origin;
vec3 lowerLeftCorner;
vec3 horizontal;
vec3 vertical;
};
//############################################################ Uniforms ####################################################################################
uniform float random[2048];
uniform vec2 resolution;
uniform Camera cam;
uniform Scene scene;
uniform int objectAmount;
//############################################################ Tools
float randf(){
return random[randomIterator++];
}
Ray getRay(Camera cam, vec2 v){
return Ray(cam.origin, normalize(cam.lowerLeftCorner+cam.horizontal*v.s+cam.vertical*v.t-cam.origin));
}
vec3 randOnBall(){
vec3 p;
do{
p = vec3(randf(), randf(), randf())*2-1;
}while(p.length() >= 1);
return p;
}
//############################################################ Signed Distance Functions
float sphereSDF(vec3 p, Object o){
return length(p-o.center)-o.radius;
}
float boxSDF(vec3 p, Object o){
vec3 q = abs(p-o.center) - o.size;
return (length(max(q, 0.0)) + min(max(q.x, max(q.y, q.z)), 0.0));
}
float sceneSDF(vec3 p, Scene s){
float dist = FLT_MAX;
for(int i = 0; i < objectAmount; i++){
switch(s.objects[i].type){
case 1:
dist = min(dist, sphereSDF(p, s.objects[i]));
break;
case 2:
dist = min(dist, boxSDF(p, s.objects[i]));
break;
default:
break;
}
}
return dist;
}
float sceneSDF(vec3 p, Scene s, inout HitRecord rec){
float dist = FLT_MAX;
for(int i = 0; i < objectAmount; i++){
float tmpDist=FLT_MAX;
switch(s.objects[i].type){
case 1:
tmpDist = sphereSDF(p, s.objects[i]);
break;
case 2:
tmpDist = boxSDF(p, s.objects[i]);
break;
default:
break;
}
if(tmpDist<dist){
dist = tmpDist;
rec.o = s.objects[i];
rec.mat = s.objects[i].material;
}
}
return dist;
}
//############################################################ Material Scatter Function
bool scatterDiffuse(Ray r, HitRecord rec, inout vec3 tmpAtt, inout Ray scattered){
tmpAtt = vec3(rec.mat.albedo);
scattered = Ray(rec.p, rec.n+randOnBall());
return true;
}
bool scatter(Ray r, HitRecord rec, inout vec3 tmpAtt, inout Ray scattered){
return scatterDiffuse(r, rec, tmpAtt, scattered); //Starting out with diffuse materials, planned to
add switch-case for different materials
}
//############################################################ Main
vec3 findSceneNormal(Scene s, vec3 p){
const float h = 0.0001; // replace by an appropriate value
const vec2 k = vec2(1,-1);
return normalize( k.xyy*sceneSDF( p + k.xyy*h, s ) +
k.yyx*sceneSDF( p + k.yyx*h, s ) +
k.yxy*sceneSDF( p + k.yxy*h, s ) +
k.xxx*sceneSDF( p + k.xxx*h, s ) );
}
float findSceneIntersect(Ray r, Scene scene, inout HitRecord rec){
float t = 0.005;
vec3 p;
for(int i = 0; i < MAX_MARCH; i++){
p = r.origin+t*r.dir;
float dist = abs(sceneSDF(p, scene, rec));
if(dist < 0.001){
rec.n = findSceneNormal(scene, p);
rec.p = p;
return t;
}else{
t += dist;
if(t >= MAX_DIST){
rec.p = r.origin+t*r.dir;
rec.n = vec3(0, 0, 0);
return -1;
}
}
}
return -1;
}
vec3 calcColor(Ray r){
vec3 color;
Material emptyMat = Material(0, vec3(0));
Object emptyO = Object(0, vec3(0), 0, vec3(0), emptyMat);
HitRecord rec = HitRecord(vec3(0), vec3(0), emptyO, emptyMat, 0);
float t = findSceneIntersect(r, scene, rec);
int reflections = 0;
vec3 att = vec3(1, 1, 1);
for(int ref = 0; ref < MAX_REFLECTIONS; ref++){
if(t != -1){
vec3 tmpAtt = vec3(0);
if(scatter(r, rec, tmpAtt, r)){
att *= tmpAtt;
t = findSceneIntersect(r, scene, rec);
reflections++;
}else {
att *= tmpAtt;
t = -1;
}
}else {
color = backColor.xyz*att;
break;
}
}
return color;
}
void main(void){
HitRecord rec = HitRecord(vec3(0), vec3(0), Object(-1, vec3(0), 0, vec3(0), Material(-1, vec3(0))), Material(-1, vec3(1, 1, 1)), 0);
#if 1 //Reflection rendering
vec3 color = vec3(0);
for(int s = 0; s < MAX_AA; s++){
vec2 uv = uvPos+(vec2(randf(), randf())*2-1)/resolution;
color += calcColor(getRay(cam, uv));
}
outColor = vec4(color/MAX_AA, 1);
#else //Coloring based on normals
Ray r = getRay(cam, uvPos);
float t = findSceneIntersect(r, scene, rec);
if(t == -1){
outColor = backColor;
}else {
outColor = vec4(rec.n*0.5+0.5, 1);
}
#endif
}
Java code where I load and compile the shader (Using LWJGL):
private static int loadShader(String file, int type) {
System.out.println("Loading shader at path: " + file);
StringBuilder shaderSource = new StringBuilder();
try{
BufferedReader reader = new BufferedReader(new FileReader(file));
String line;
while((line = reader.readLine())!=null){
shaderSource.append(line).append("//\n");
}
reader.close();
}catch(IOException e){
e.printStackTrace();
System.exit(-1);
}
int shaderID = glCreateShader(type);
glShaderSource(shaderID, shaderSource);
glCompileShader(shaderID);
if(glGetShaderi(shaderID, GL_COMPILE_STATUS )== GL_FALSE){
System.out.println(glGetShaderInfoLog(shaderID, 500));
System.err.println("Could not compile shader!");
System.exit(-1);
}
return shaderID;
}
The function loadShader in my code does not give me any error as it does with syntax errors and doesn't exit the program, thus GL_COMPILE_STATUS is not false.
I am fully aware nested for loops and my use of conditionals is far from efficient performance-wise, but I would expect it to be slow, not completely broken. I am running this on an Intel UHD 630, which according to https://www.intel.ca/content/www/ca/en/support/products/98909/graphics/graphics-for-7th-generation-intel-processors/intel-hd-graphics-630.html supports openGL 4.4. Therefore, according to GLSL maximum number of instructions, I should have access to 65536 instructions in my frag shader and fully dynamic branching. For these reasons, I don't think instruction limit is the problem and any help would be greatly appreciated. If you need any more information, I'll add it as soon as possible. If the CPU code is necessary, I can add it too, but I don't think it's the issue, as changing only the shader can trigger this bug.
Edit 1:
Both glGetShaderInfoLog and glGetProgramInfoLog return nothing when called after the program has been validated and linked.

Text rendering not working correctly

so I'm trying to render some text using FTGL, GLFW and GLEW libraries in my C++ engine following this tutorial: https://www.youtube.com/watch?v=Rsdc6I80aFQ.
But when I do so where the text should be instead of characters I get triangles of some kind (screenshot):
I wanted to use the same shader as when I'm rendering normal sprites, so I set the model matrix in shader to identity matrix and instead I'm passing character positions to the shader with VBO.
Here's font class:
class Font
{
private:
/* Variables */
ftgl::texture_atlas_t* m_atlas;
ftgl::texture_font_t* m_font;
public:
/* Constructors */
Font(const char* filename, const float size)
: m_atlas(ftgl::texture_atlas_new(512, 512, 1)),
m_font(ftgl::texture_font_new_from_file(m_atlas, size, filename))
{}
/* Functions */
void bindFontTexture() const
{
GLcall( glBindTexture(GL_TEXTURE_2D, m_atlas->id) );
}
// Getters
inline ftgl::texture_font_t* getFTGLFont() const { return m_font; }
};
And here is label class (labels are 2D renderables with some text in my engine):
class Label2D : public Renderable2D
{
protected:
/* Variables */
std::string m_text;
Color m_color;
Font* m_font;
public:
/* Constructors */
Label2D(const std::string& text, const vector2& pos, const vector2& size, Font* font)
: Renderable2D(pos, size), m_text(text), m_font(font)
{}
Label2D(const std::string& text, const vector2& pos, const vector2& size, Font* font, const Color& color)
: Renderable2D(pos, size), m_text(text), m_font(font), m_color(color)
{}
/* Functions */
void bindFontTexture() const
{
m_font->bindFontTexture();
}
// Setters
void setText(const std::string& text)
{
m_text = text;
}
void setColor(const Color& color)
{
m_color = color;
}
void setFont(Font* font)
{
m_font = font;
}
// Getters
inline const std::string& getText() const { return m_text; }
inline const Color& getColor() const { return m_color; }
inline ftgl::texture_font_t* getFTGLFont() const { return m_font->getFTGLFont(); }
};
Here's my text rendering code. Now I DO know this code is written terribly since I shouldn't create a VAO and VBO everytime I want to render some text, but I did it only for test purposes, I just want to be sure that library is working fine, then I will organize the code:
void Layer2D::m_renderLabels(const bool renderLighting)
{
// Uniforms Setup
m_textureShader.start();
m_textureShader.setUniformMatrix4f("u_modelMatrix", matrix4(1.0f));f));
// Rendering Labels from std::vector
for (Label2D* label : m_labels)
{
// Binding
m_textureVAO.bind();
label->bindFontTexture();
// Starting Shader
m_textureShader.start();
// Preparation Stuff
std::vector<float> positions;
std::vector<float> textureCoordinates;
float offsetX = label->getPosition().x;
// Loading Characters
for (unsigned int i = 0; i < label->getText().length(); i++)
{
// Loading Glyph
ftgl::texture_glyph_t* glyph = ftgl::texture_font_get_glyph(label->getFTGLFont(), &label->getText().at(i));
if (glyph != NULL)
{
if (i > 0)
{
float kerning = texture_glyph_get_kerning(glyph, &label->getText().at(i - 1));
offsetX += kerning;
}
// Glyph Position/Offset/Size Calculations
float x0 = offsetX + glyph->offset_x;
float y0 = label->getPosition().y + glyph->offset_y;
float x1 = x0 + glyph->width;
float y1 = y0 - glyph->height;
float u0 = glyph->s0;
float v0 = glyph->t0;
float u1 = glyph->s1;
float v1 = glyph->t1;
positions.push_back(x0);
positions.push_back(y0);
positions.push_back(x0);
positions.push_back(y1);
positions.push_back(x1);
positions.push_back(y1);
positions.push_back(x1);
positions.push_back(y0);
textureCoordinates.push_back(u0);
textureCoordinates.push_back(v0);
textureCoordinates.push_back(u0);
textureCoordinates.push_back(v1);
textureCoordinates.push_back(u1);
textureCoordinates.push_back(v1);
textureCoordinates.push_back(u1);
textureCoordinates.push_back(v0);
offsetX += glyph->advance_x;
}
}
VertexArray textVAO;
VertexBuffer* textPosVBO = new VertexBuffer(&positions[0], positions.size(), 2);
VertexBuffer* textTexCoordsVBO = new VertexBuffer(&textureCoordinates[0], textureCoordinates.size(), 2);
textVAO.addAttribute(textPosVBO, 0);
textVAO.addAttribute(textTexCoordsVBO, 1);
textVAO.bind();
// Rendering
GLcall( glDrawArrays(GL_TRIANGLE_STRIP, 0, 4 * label->getText().length()) );
}
}
And lastly that's the shader code:
Vertex:
#shader vertex
#version 330 core
layout(location = 0) in vec2 in_position;
layout(location = 1) in vec2 in_textureCoordinates;
out vec2 pass_textureCoordinates;
uniform mat4 u_projectionMatrix;
uniform mat4 u_viewMatrix;
uniform mat4 u_modelMatrix;
void main()
{
pass_textureCoordinates = in_textureCoordinates;
gl_Position = u_projectionMatrix * u_viewMatrix * u_modelMatrix * vec4(in_position, 0.0, 1.0);
}
And fragment:
#shader fragment
#version 330 core
in vec2 pass_textureCoordinates;
out vec4 out_color;
uniform sampler2D u_textureSampler;
void main()
{
vec4 textureColor = texture2D(u_textureSampler, pass_textureCoordinates);
out_color = textureColor;
out_color = vec4(1.0, 0.0, 0.0, 1.0); //done for test purposes only (it still doesn't work when this line is removed)
}
I'm sorry if I wasn't specific enough, if I could provide any more information to help You help me, just please comment down below.
Thanks in advance!
EDIT:
If You were wondering, I do set projection and view matrices in other parts of code:
m_textureShader.start();
m_textureShader.setUniformMatrix4f("u_projectionMatrix", m_projectionMatrix);
m_textureShader.setUniformMatrix4f("u_viewMatrix", viewMatrix);
EDIT: After swapping the last 2 vertices I get quads now, but still not a text (screenshot) The quads aren't red because I removed the debugging line from fragment shader.

glsl UNEXPECTED NEW_IDENTIFIER, expecting $end

I'm experimenting this frustrating error while compiling a fragment shader. If I try to compile the shader as it is :
#version 450 core
in vec2 tex_coord;
in flat int vertex_id;
out vec4 color;
layout (binding = 20) uniform sampler2D buildingTex;
layout (binding = 21) uniform sampler2D groundTex;
const int cube_vertices = 36;
const int ground_vertices = 4;
void main(void)
{
if(vertex_id < cube_vertices)
{
float scaleF = .5f;
vec2 scale = vec2(scaleF,scaleF);
color = texture(buildingTex,tex_coord*scale);
}
if(vertex_id >= cube_vertices)
{
color = texture(groundTex,tex_coord);
}
}
the compailer complains with : glsl UNEXPECTED NEW_IDENTIFIER, expecting $end.
However if I add to my fragment shader another fragment shader, that i had in another file , and then i comment all its rows like follows :
// #version 450 core
// in vec2 tex_coord;
// in flat int vertex_id;
// out vec4 color;
// layout (binding = 20) uniform sampler2D buildingTex;
// layout (binding = 21) uniform sampler2D groundTex;
// int cube_vertices;
// int ground_vertices;
// void main(void)
// {
// if(vertex_id < cube_vertices)
// {
// float scaleF = .5f;
// vec2 scale = vec2(scaleF,scaleF);
// color = texture(buildingTex,tex_coord*scale);
// //color = vec4(1.0,1.0,1.0,1.0);
// }
// if(vertex_id >= cube_vertices)
// {
// color = texture(groundTex,tex_coord);
// //color = vec4(1.0,0.0,0.0,1.0);
// }
#version 450 core
in vec2 tex_coord;
in flat int vertex_id;
out vec4 color;
layout (binding = 20) uniform sampler2D buildingTex;
layout (binding = 21) uniform sampler2D groundTex;
const int cube_vertices = 36;
const int ground_vertices = 4;
void main(void)
{
if(vertex_id < cube_vertices)
{
float scaleF = .5f;
vec2 scale = vec2(scaleF,scaleF);
color = texture(buildingTex,tex_coord*scale);
}
if(vertex_id >= cube_vertices)
{
color = texture(groundTex,tex_coord);
}
}
in this case the shader is compiled!
I tried to remove the commented line of the old code, and seems like I can remove just some of them, while others I cannot touch, to have the shader compiled.
At this moment the shader is :
// #version 450 core
// in vec2 tex_coord;
// in flat int vertex_id;
// out vec4 color;
// layout (binding = 20) uniform sampler2D buildingTex;
// layout (binding = 21) uniform sampler2D groundTex;
// int cube_vertices;
// if(vertex_id < cube_vertices)
#version 450 core
in vec2 tex_coord;
in flat int vertex_id;
out vec4 color;
layout (binding = 20) uniform sampler2D buildingTex;
layout (binding = 21) uniform sampler2D groundTex;
const int cube_vertices = 36;
const int ground_vertices = 4;
void main(void)
{
if(vertex_id < cube_vertices)
{
float scaleF = .5f;
vec2 scale = vec2(scaleF,scaleF);
color = texture(buildingTex,tex_coord*scale);
}
if(vertex_id >= cube_vertices)
{
color = texture(groundTex,tex_coord);
}
}
and seems I cannot remove anymore from the commented code.
Since this seems the most illogical problem I ever had in my life,and since the others questions having this same error warning, were solved in a way that doesn't match my case, I'm opening a new quest.
ps: I tried to make a completely new shader but it didn't work.
pps: the problem comes up after I changed the major-mode (through M-x and not modifying the init file of emacs!) of emacs in order to use the ordinary shortcut I use in cpp files, in the shaders files (.vert, .frag ...), however even restoring the default-mode and restarting emacs the shader does not compile!
EDIT: this is the method I use to load the shader
const char* Shader::loadShader(std::string shaderPath)
{
std::ifstream temp_ss(shaderPath,std::ifstream::in);
if(temp_ss)
{
char c;
char *ss = new char[shader_size];
unsigned i = 0 ;
while(temp_ss.get(c))
{
ss[i++] = c;
}
std::cout<<std::ends;
//ends adds a null, and then flushes the buffer
//if i don't use it , often, the window does not show anything
//I could use std::cout<<std::endl; in place of ends,
//but ends seem a more polite solution to me
//CHECK SHADER
for(int i = 0; i < shader_size; i++)
if(ss[i] == '\0')
std::cout<<"shader.cpp::ERROR: NULL CHARACTER FOUND ON SHADER "<<
shaderPath<<std::endl;
return ss;
}
else
std::clog<<"shader.cpp::loadShader() : ERROR ::: no shaders found at the path : "<<shaderPath<<std::endl;
}
From the OP:
TO SUMMARIZE : shader files MUST be null terminated , I modify the function that loads the shader source into the buffer like follows:
const char* Shader::loadShader(std::string shaderPath)
{
std::ifstream temp_ss(shaderPath,std::ifstream::in);
if(temp_ss)
{
char c;
char *ss = new char[shader_size];
unsigned i = 0 ;
while(temp_ss.get(c))
{
ss[i++] = c;
}
//CHECK SHADER
bool nullTerminated = false;
for(int i = 0; i < shader_size; i++)
if(ss[i] == '\0')
nullTerminated = true;
if(!nullTerminated)
ss[shader_size++] = '\0';
return ss;
}
else
std::clog<<"shader.cpp::loadShader() : ERROR ::: no shaders found at the path : "<<shaderPath<<std::endl;
}
and the fragment shader compiles!

Attribute Position_VS_in is bound to generic attribute 0, but gl_Vertex is also used

I am attempting to use four shaders: A vertex Shader (VS), a Tessellation Control Shader (TCS), a Tessellation Evaluation Shader (TES), and a Fragment Shader (FS).
However, when I attempt to load these shaders, I get:
This has been most baffling, and I have already spent a couple of hours trying to sort it out. However, I cannot see "gl_Vertex" anywhere in my code, so I am stuck really. I don't even know which shader is the problem.
Here is my current code for loading shaders (It looks like a lot, but it's really all just repeated code with slight changes):
public static int loadShaderPair(String vertexShaderLocation, String fragmentShaderLocation, String tesShaderLocation, String tesscontrolShaderLocation) {
int shaderProgram = glCreateProgram();
int vertexShader = glCreateShader(GL_VERTEX_SHADER);
int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
int tesscontrolShader = glCreateShader(GL40.GL_TESS_CONTROL_SHADER);
int tesShader = glCreateShader(GL40.GL_TESS_EVALUATION_SHADER);
StringBuilder vertexShaderSource = new StringBuilder();
StringBuilder fragmentShaderSource = new StringBuilder();
StringBuilder tesShaderSource = new StringBuilder();
StringBuilder tesscontrolShaderSource = new StringBuilder();
BufferedReader tesShaderFileReader = null;
try {
tesShaderFileReader = new BufferedReader(new FileReader(tesShaderLocation));
String line;
while ((line = tesShaderFileReader.readLine()) != null) {
tesShaderSource.append(line).append('\n');
}
} catch (IOException e) {
e.printStackTrace();
return -1;
} finally {
if (tesShaderFileReader != null) {
try {
tesShaderFileReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
BufferedReader tesscontrolShaderFileReader = null;
try {
tesscontrolShaderFileReader = new BufferedReader(new FileReader(tesscontrolShaderLocation));
String line;
while ((line = tesscontrolShaderFileReader.readLine()) != null) {
tesscontrolShaderSource.append(line).append('\n');
}
} catch (IOException e) {
e.printStackTrace();
return -1;
} finally {
if (tesscontrolShaderFileReader != null) {
try {
tesscontrolShaderFileReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
BufferedReader vertexShaderFileReader = null;
try {
vertexShaderFileReader = new BufferedReader(new FileReader(vertexShaderLocation));
String line;
while ((line = vertexShaderFileReader.readLine()) != null) {
vertexShaderSource.append(line).append('\n');
}
} catch (IOException e) {
e.printStackTrace();
return -1;
} finally {
if (vertexShaderFileReader != null) {
try {
vertexShaderFileReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
BufferedReader fragmentShaderFileReader = null;
try {
fragmentShaderFileReader = new BufferedReader(new FileReader(fragmentShaderLocation));
String line;
while ((line = fragmentShaderFileReader.readLine()) != null) {
fragmentShaderSource.append(line).append('\n');
}
} catch (IOException e) {
e.printStackTrace();
return -1;
} finally {
if (fragmentShaderFileReader != null) {
try {
fragmentShaderFileReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
glShaderSource(vertexShader, vertexShaderSource);
glCompileShader(vertexShader);
if (glGetShaderi(vertexShader, GL_COMPILE_STATUS) == GL_FALSE) {
System.err.println("Vertex shader wasn't able to be compiled correctly. Error log:");
System.err.println(glGetShaderInfoLog(vertexShader, 1024));
return -1;
}
glShaderSource(tesscontrolShader, tesscontrolShaderSource);
glCompileShader(tesscontrolShader);
if (glGetShaderi(tesscontrolShader, GL_COMPILE_STATUS) == GL_FALSE) {
System.err.println("tesscontrol shader wasn't able to be compiled correctly. Error log:");
System.err.println(GL11.glGetString(GL11.glGetError()));
System.err.println(glGetShaderInfoLog(tesscontrolShader, 1024));
}
glShaderSource(fragmentShader, fragmentShaderSource);
glCompileShader(fragmentShader);
if (glGetShaderi(fragmentShader, GL_COMPILE_STATUS) == GL_FALSE) {
System.err.println("Fragment shader wasn't able to be compiled correctly. Error log:");
System.err.println(GL11.glGetString(GL11.glGetError()));
System.err.println(glGetShaderInfoLog(fragmentShader, 1024));
}
glShaderSource(tesShader, tesShaderSource);
glCompileShader(tesShader);
if (glGetShaderi(vertexShader, GL_COMPILE_STATUS) == GL_FALSE) {
System.err.println("Tessellation shader wasn't able to be compiled correctly. Error log:");
System.err.println(glGetShaderInfoLog(tesShader, 1024));
return -1;
}
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, tesShader);
glAttachShader(shaderProgram, tesscontrolShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
if (glGetProgrami(shaderProgram, GL_LINK_STATUS) == GL_FALSE) {
System.err.println("Shader program wasn't linked correctly.");
System.err.println(GL11.glGetString(GL11.glGetError()));
System.err.println(glGetProgramInfoLog(shaderProgram, 1024));
System.exit(1);
return -1;
}
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
glDeleteShader(tesShader);
glDeleteShader(tesscontrolShader);
return shaderProgram;
}
And here are my shaders:
VS:
layout (location = 0) in vec3 Position_VS_in;
layout (location = 1) in vec2 TexCoord_VS_in;
layout (location = 2) in vec3 Normal_VS_in;
uniform mat4 gWorld;
out vec3 WorldPos_CS_in;
out vec2 TexCoord_CS_in;
out vec3 Normal_CS_in;
void main()
{
WorldPos_CS_in = (gWorld * vec4(Position_VS_in, 1.0)).xyz;
TexCoord_CS_in = TexCoord_VS_in;
Normal_CS_in = (gWorld * vec4(Normal_VS_in, 0.0)).xyz;
gl_Position = ftransform();
}
TCS:
// define the number of CPs in the output patch
layout (vertices = 3) out;
uniform vec3 gEyeWorldPos;
// attributes of the input CPs
in vec3 WorldPos_CS_in[];
in vec2 TexCoord_CS_in[];
in vec3 Normal_CS_in[];
// attributes of the output CPs
out vec3 WorldPos_ES_in[];
out vec2 TexCoord_ES_in[];
out vec3 Normal_ES_in[];
float GetTessLevel(float Distance0, float Distance1)
{
float AvgDistance = (Distance0 + Distance1) / 2.0;
if (AvgDistance <= 2.0) {
return 10.0;
}
else if (AvgDistance <= 5.0) {
return 7.0;
}
else {
return 3.0;
}
}
void main()
{
// Set the control points of the output patch
TexCoord_ES_in[gl_InvocationID] = TexCoord_CS_in[gl_InvocationID];
Normal_ES_in[gl_InvocationID] = Normal_CS_in[gl_InvocationID];
WorldPos_ES_in[gl_InvocationID] = WorldPos_CS_in[gl_InvocationID];
// Calculate the distance from the camera to the three control points
float EyeToVertexDistance0 = distance(gEyeWorldPos, WorldPos_ES_in[0]);
float EyeToVertexDistance1 = distance(gEyeWorldPos, WorldPos_ES_in[1]);
float EyeToVertexDistance2 = distance(gEyeWorldPos, WorldPos_ES_in[2]);
// Calculate the tessellation levels
gl_TessLevelOuter[0] = GetTessLevel(EyeToVertexDistance1, EyeToVertexDistance2);
gl_TessLevelOuter[1] = GetTessLevel(EyeToVertexDistance2, EyeToVertexDistance0);
gl_TessLevelOuter[2] = GetTessLevel(EyeToVertexDistance0, EyeToVertexDistance1);
gl_TessLevelInner[0] = gl_TessLevelOuter[2];
}
TES:
layout(triangles, equal_spacing, ccw) in;
uniform mat4 gVP;
uniform sampler2D gDisplacementMap;
uniform float gDispFactor;
in vec3 WorldPos_ES_in[];
in vec2 TexCoord_ES_in[];
in vec3 Normal_ES_in[];
out vec3 WorldPos_FS_in;
out vec2 TexCoord_FS_in;
out vec3 Normal_FS_in;
vec2 interpolate2D(vec2 v0, vec2 v1, vec2 v2)
{
return vec2(gl_TessCoord.x) * v0 + vec2(gl_TessCoord.y) * v1 + vec2(gl_TessCoord.z) * v2;
}
vec3 interpolate3D(vec3 v0, vec3 v1, vec3 v2)
{
return vec3(gl_TessCoord.x) * v0 + vec3(gl_TessCoord.y) * v1 + vec3(gl_TessCoord.z) * v2;
}
void main()
{
// Interpolate the attributes of the output vertex using the barycentric coordinates
TexCoord_FS_in = interpolate2D(TexCoord_ES_in[0], TexCoord_ES_in[1], TexCoord_ES_in[2]);
Normal_FS_in = interpolate3D(Normal_ES_in[0], Normal_ES_in[1], Normal_ES_in[2]);
Normal_FS_in = normalize(Normal_FS_in);
WorldPos_FS_in = interpolate3D(WorldPos_ES_in[0], WorldPos_ES_in[1], WorldPos_ES_in[2]);
// Displace the vertex along the normal
float Displacement = texture(gDisplacementMap, TexCoord_FS_in.xy).x;
WorldPos_FS_in += Normal_FS_in * Displacement * gDispFactor;
gl_Position = gVP * vec4(WorldPos_FS_in, 1.0);
}
FS:
varying vec4 position_in_view_space;
uniform sampler2D color_texture;
void main()
{
float dist = distance(position_in_view_space,
vec4(0.0, 0.0, 0.0, 1.0));
if (dist < 1000.0)
{
gl_FragColor = gl_Color;
// color near origin
}
else
{
gl_FragColor = gl_Color;
//kill;
//discard;
//gl_FragColor = vec4(0.3, 0.3, 0.3, 1.0);
// color far from origin
}
}
If for any reason it helps, my PC supports up to (and including) OpenGL 4.0.
Since I have been unable to work out how I am supposedly using a line of code I am not using, I am hoping one of you will be able to diagnose the issue.
Well, the error message is very clear, and the reason is this:
layout (location = 0) in vec3 Position_VS_in;
[...]
gl_Position = ftransform();
ftransform is a legacy function which uses the legacy bultin vertex position attribute gl_Vertex to transform a vertex according to the also legacy bultin matrix uniforms.
The GL spec also requires the generic vertex attribute 0 to alias the old builtin gl_Vertex, so your attribute index 0 is blocked by this and you must not assign some other attribute to this.
Now it is totally unclear why you use ftransform here. It seems like you don't want that at all. But I can't be sure about that. But it will definitively not work that way. You shouldn't use that deprecated stuff at all, in my opinion.
UPDATE 1
Note that ftransform is in princicple equivalent to gl_ModelViewProjectionMatrix * gl_Vertex, with gl_ModelViewProjectionMatrix being a builtin uniform representing the product P * MV of the matrices you set for GL_PROJECTION and GL_MODELVIEW using the also-deprecated GL matrix stack, and gl_Vertex being the builtin vertex position attribute set with glVertex.../glVertexPointer/glVertexAttribPointer(0,...). Both can be replaced by user-defined attributes/uniforms.
The only twist with ftransform is that it is also guaranteed to get the exact same result as rendering with the fixed function pipeline does, for the same inputs. That was useful for multi-pass algorithms where some passed needed to render with shaders, while others used the fixed function pipelinew. Such a function was needed because there might be slight numerical differences if the shader does not use the exact same formula as the GL implementation when rendering without shaders.

Why is this glsl shader causing an "Invalid Operation" when trying to use the program?

I'm checking the logs, and everything compiles fine. The shader looks like this:
Vertex:
#version 330 core
layout(location = 0) in struct InData {
vec3 position;
vec4 color;
} inData;
out struct OutData {
vec3 position;
vec4 color;
} outData;
void main()
{
outData.position = inData.position;
outData.color = inData.color;
}
Fragment:
#version 330 core
in struct InData {
vec2 position;
vec4 color;
} inData;
out vec4 color;
void main(){
color = inData.color;
}
I'm preparing the shader like this:
public Shader(string src, ShaderType type)
{
shaderId = GL.CreateShader(type);
GL.ShaderSource(shaderId, GetShader(src));
GL.CompileShader(shaderId);
EventSystem.Log.Message(GL.GetShaderInfoLog(shaderId));
EventSystem.Log.Message("GLERROR: " + GL.GetError());
packs = new List<ShaderPack>();
}
public void Attach(ShaderPack pack)
{
packs.Add(pack);
GL.AttachShader(pack.ProgramID, shaderId);
EventSystem.Log.Message(GL.GetProgramInfoLog(pack.ProgramID));
EventSystem.Log.Message("GLERROR: " + GL.GetError());
}
Then I compile the shader:
public void Compile()
{
if(program >= 0)
GL.DeleteProgram(program);
program = GL.CreateProgram();
foreach (var s in shaders.Values)
s.Attach(this);
EventSystem.Log.Message(GL.GetProgramInfoLog(program));
EventSystem.Log.Message("GLERROR: " + GL.GetError());
}
And then I'm trying to use it:
mesh = new Mesh();
mesh.AddTriangle(
new Vector3(0, 0, 0), new Vector4(1, 0, 0, 1),
new Vector3(0, sizeY, 0), new Vector4(0, 1, 0, 1),
new Vector3(sizeX, sizeY, 0), new Vector4(0, 0, 1, 1));
mesh.RefreshBuffer();
shaderPack.Apply();
shaderPack.SetVertexAttribute<Mesh.MeshData1>("vertex", 0, mesh.meshData);
EventSystem.Log.Message("GLERROR: " + GL.GetError());
In Apply GL.UseProgram is called and GetError returns "Invalid Operation"
UPDATE:
Okay I changed the code:
public void Compile()
{
if(program >= 0)
GL.DeleteProgram(program);
program = GL.CreateProgram();
foreach (var s in shaders.Values)
s.Attach(this);
// GL.LinkProgram(program);
//GL.ValidateProgram(program);
GL.ValidateProgram(program);
EventSystem.Log.Message("Validate: " + GL.GetProgramInfoLog(program) + " - " + GL.GetError());
}
public void Apply()
{
GL.UseProgram(program);
EventSystem.Log.Message("GLERROR (Apply): " + GL.GetError());
}
And the output is
[23:25:55][Log]: Validate: - NoError
[23:25:55][Log]: GLERROR (Apply): InvalidOperation
edit: Okay I changed the vertex shaders:
#version 330 core
layout(location = 0) in struct InData {
vec3 position;
vec4 color;
} inData;
void main()
{
gl_Position = vec4(inData.position, 1);
}
...
#version 330 core
//in struct InData {
// vec2 position;
// vec4 color;
//} inData;
out vec4 color;
void main(){
color = vec4(1,0,0,1);
}
It compiles without errors, but I have a blank screen...
Pre-Rollback:
EDIT: Okay, I suspect the problem lies here:
public void VertexAttribute<T>(int loc, ShaderPack p, T[] dataArray) where T : struct
{
int buf;
GL.GenBuffers(1, out buf);
GL.BindBuffer(BufferTarget.ArrayBuffer, buf);
GL.BufferData<T>(BufferTarget.ArrayBuffer, (IntPtr)(dataArray.Length * Marshal.SizeOf(typeof(T))), dataArray, BufferUsageHint.StaticDraw);
GL.EnableVertexAttribArray(0);
GL.VertexAttribPointer<T>(loc, 2, VertexAttribPointerType.Float, false, 0, ref dataArray[0]);
GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
}
I'm passing this with an Array of the following type:
public struct MeshData1
{
public Vector3 vertex;
public Vector4 color;
public MeshData1(Vector3 vertex, Vector4 color)
{
this.vertex = vertex;
this.color = color;
}
}
And the input looks like this:
#version 330 core
layout(location = 0) in struct InData {
vec3 position;
vec4 color;
} inData;
void main()
{
gl_Position = vec4(inData.position, 1.0);
}
What am I doing wrong?
Two problems immediately come to mind:
You never linked the attached shader stages in your program object (most important)
The string output by glGetProgramInfoLog (...) is only generated/updated after linking or validating a GLSL program.
To fix this, you should make a call to glLinkProgram (...) after attaching your shaders, and also understand that up until you do this the program info log will be undefined.
glValidateProgram (...) is another way of updating the contents of the program info log. In addition to generating the info log, this function will also return whether your program is in a state suitable for execution or not. The result of this operation is stored in a per-program state called GL_VALIDATE_STATUS.