我正在片段着色器中编写一个基本的 Sphere-Tracer,如果我只是根据它们的表面法线为点着色,一切都很好,但是一旦我尝试实现反射算法,编译需要更长的时间,而且只是黑屏。fps 从 1 开始,但很快上升到 1000,这是我设置的代码限制,告诉我着色器实际上没有做任何事情,只是忽略了我的代码,这应该比这慢得多。我最初以为我达到了 GPU 的指令限制,但我不认为这是问题所在,因为使用正常着色代码,我可以设置MAX_MARCH
为高值,如 1000,但使用反射,即使使用MAX_REFLECTIONS
和MAX_AA
到一个,这应该类似于正常着色的指令量(可能更多〜50,我认为不重要)。但是我需要将其设置MAX_MARCH
为 1 才能渲染,即使将其设置为 2 也会导致该错误。
顶点着色器:
//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);
}
片段着色器:
#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 代码(使用 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;
}
我的代码中的函数 loadShader 不会像语法错误那样给我任何错误,并且不会退出程序,因此GL_COMPILE_STATUS
不是错误的。
我完全意识到嵌套的 for 循环,并且我对条件的使用远非高效的性能,但我希望它会很慢,而不是完全破坏。我在英特尔 UHD 630 上运行它,根据https://www.intel.ca/content/www/ca/en/support/products/98909/graphics/graphics-for-7th-generation-intel-processors /intel-hd-graphics-630.html支持 openGL 4.4。因此,根据GLSL 最大指令数,我应该可以在我的片段着色器和完全动态分支中访问 65536 条指令。由于这些原因,我不认为指令限制是问题,任何帮助将不胜感激。如果您需要更多信息,我会尽快添加。如果需要 CPU 代码,我也可以添加它,但我认为这不是问题,因为仅更改着色器会触发此错误。
编辑 1:在程序经过验证和链接后调用时,两者都没有返回任何内容glGetShaderInfoLog
。glGetProgramInfoLog