216

我需要调试一个 GLSL 程序,但我不知道如何输出中间结果。是否可以使用 GLSL 进行一些调试跟踪(例如使用 printf)?

4

13 回答 13

137

您无法从 GLSL 中轻松地与 CPU 进行通信。使用 glslDevil 或其他工具是您最好的选择。

printf 需要尝试从运行 GLSL 代码的 GPU 回到 CPU。相反,您可以尝试向前推进显示器。与其尝试输出文本,不如输出一些在屏幕上视觉上与众不同的东西。例如,只有当您到达要添加 printf 的代码点时,您才能将某些东西绘制成特定的颜色。如果您需要打印一个值,您可以根据该值设置颜色。

于 2010-03-24T17:14:44.227 回答
64
void main(){
  float bug=0.0;
  vec3 tile=texture2D(colMap, coords.st).xyz;
  vec4 col=vec4(tile, 1.0);

  if(something) bug=1.0;

  col.x+=bug;

  gl_FragColor=col;
}
于 2011-10-13T00:56:44.423 回答
14

我发现变换反馈是调试顶点着色器的有用工具。您可以使用它来捕获 VS 输出的值,并在 CPU 端将它们读回,而无需通过光栅化器。

是有关转换反馈的教程的另一个链接。

于 2012-11-23T21:11:59.210 回答
10

GLSL Sandbox对我来说对于着色器来说非常方便。

本身不是调试(已被回答为无能力),但可以方便地快速查看输出的变化。

于 2015-05-18T01:28:02.687 回答
8

如果你想在屏幕上可视化一个值的变化,你可以使用类似这样的热图函数(我用 hlsl 写的,但是很容易适应 glsl):

float4 HeatMapColor(float value, float minValue, float maxValue)
{
    #define HEATMAP_COLORS_COUNT 6
    float4 colors[HEATMAP_COLORS_COUNT] =
    {
        float4(0.32, 0.00, 0.32, 1.00),
        float4(0.00, 0.00, 1.00, 1.00),
        float4(0.00, 1.00, 0.00, 1.00),
        float4(1.00, 1.00, 0.00, 1.00),
        float4(1.00, 0.60, 0.00, 1.00),
        float4(1.00, 0.00, 0.00, 1.00),
    };
    float ratio=(HEATMAP_COLORS_COUNT-1.0)*saturate((value-minValue)/(maxValue-minValue));
    float indexMin=floor(ratio);
    float indexMax=min(indexMin+1,HEATMAP_COLORS_COUNT-1);
    return lerp(colors[indexMin], colors[indexMax], ratio-indexMin);
}

然后在您的像素着色器中,您只需输出如下内容:

return HeatMapColor(myValue, 0.00, 50.00);

并且可以了解它在您的像素中如何变化:

在此处输入图像描述

当然,您可以使用任何您喜欢的颜色。

于 2015-04-23T07:13:24.417 回答
6

您可以试试这个:https ://github.com/msqrt/shader-printf这是一个适当地称为“GLSL 的简单 printf 功能”的实现。

您可能还想尝试 ShaderToy,或者观看“代码的艺术”YouTube 频道中的类似视频 ( https://youtu.be/EBrAdahFtuo ),您可以在其中看到一些适用于调试和可视化。我强烈推荐他的频道,因为他写了一些非常好的东西,而且他还擅长以新颖、引人入胜且易于消化的格式呈现复杂的想法(他的 Mandelbrot 视频就是一个很好的例子:https:// youtu.be/6IWXkV82oyY )

我希望没有人介意这个迟到的回复,但是这个问题在谷歌搜索 GLSL 调试时排名很高,而且在 9 年内当然发生了很大变化:-)

PS:其他替代品也可能是 NVIDIA nSight 和 AMD ShaderAnalyzer,它们为着色器提供了完整的步进调试器。

于 2019-06-26T10:29:40.137 回答
3

在这个答案的底部是一个 GLSL 代码的例子,它允许将完整的float值输出为颜色,编码 IEEE 754 binary32。我使用它如下(这个片段给出了yy模型视图矩阵的组件):

vec4 xAsColor=toColor(gl_ModelViewMatrix[1][1]);
if(bool(1)) // put 0 here to get lowest byte instead of three highest
    gl_FrontColor=vec4(xAsColor.rgb,1);
else
    gl_FrontColor=vec4(xAsColor.a,0,0,1);

在屏幕上显示此内容后,您可以使用任何颜色选择器,将颜色格式化为 HTML(如果您不需要更高的精度,则附加00到该值,如果需要,则进行第二次传递以获取较低的字节),并且rgb你得到floatIEEE 754的十六进制表示binary32

这是 的实际实现toColor()

const int emax=127;
// Input: x>=0
// Output: base 2 exponent of x if (x!=0 && !isnan(x) && !isinf(x))
//         -emax if x==0
//         emax+1 otherwise
int floorLog2(float x)
{
    if(x==0.) return -emax;
    // NOTE: there exist values of x, for which floor(log2(x)) will give wrong
    // (off by one) result as compared to the one calculated with infinite precision.
    // Thus we do it in a brute-force way.
    for(int e=emax;e>=1-emax;--e)
        if(x>=exp2(float(e))) return e;
    // If we are here, x must be infinity or NaN
    return emax+1;
}

// Input: any x
// Output: IEEE 754 biased exponent with bias=emax
int biasedExp(float x) { return emax+floorLog2(abs(x)); }

// Input: any x such that (!isnan(x) && !isinf(x))
// Output: significand AKA mantissa of x if !isnan(x) && !isinf(x)
//         undefined otherwise
float significand(float x)
{
    // converting int to float so that exp2(genType) gets correctly-typed value
    float expo=float(floorLog2(abs(x)));
    return abs(x)/exp2(expo);
}

// Input: x\in[0,1)
//        N>=0
// Output: Nth byte as counted from the highest byte in the fraction
int part(float x,int N)
{
    // All comments about exactness here assume that underflow and overflow don't occur
    const float byteShift=256.;
    // Multiplication is exact since it's just an increase of exponent by 8
    for(int n=0;n<N;++n)
        x*=byteShift;

    // Cut higher bits away.
    // $q \in [0,1) \cap \mathbb Q'.$
    float q=fract(x);

    // Shift and cut lower bits away. Cutting lower bits prevents potentially unexpected
    // results of rounding by the GPU later in the pipeline when transforming to TrueColor
    // the resulting subpixel value.
    // $c \in [0,255] \cap \mathbb Z.$
    // Multiplication is exact since it's just and increase of exponent by 8
    float c=floor(byteShift*q);
    return int(c);
}

// Input: any x acceptable to significand()
// Output: significand of x split to (8,8,8)-bit data vector
ivec3 significandAsIVec3(float x)
{
    ivec3 result;
    float sig=significand(x)/2.; // shift all bits to fractional part
    result.x=part(sig,0);
    result.y=part(sig,1);
    result.z=part(sig,2);
    return result;
}

// Input: any x such that !isnan(x)
// Output: IEEE 754 defined binary32 number, packed as ivec4(byte3,byte2,byte1,byte0)
ivec4 packIEEE754binary32(float x)
{
    int e = biasedExp(x);
    // sign to bit 7
    int s = x<0. ? 128 : 0;

    ivec4 binary32;
    binary32.yzw=significandAsIVec3(x);
    // clear the implicit integer bit of significand
    if(binary32.y>=128) binary32.y-=128;
    // put lowest bit of exponent into its position, replacing just cleared integer bit
    binary32.y+=128*int(mod(float(e),2.));
    // prepare high bits of exponent for fitting into their positions
    e/=2;
    // pack highest byte
    binary32.x=e+s;

    return binary32;
}

vec4 toColor(float x)
{
    ivec4 binary32=packIEEE754binary32(x);
    // Transform color components to [0,1] range.
    // Division is inexact, but works reliably for all integers from 0 to 255 if
    // the transformation to TrueColor by GPU uses rounding to nearest or upwards.
    // The result will be multiplied by 255 back when transformed
    // to TrueColor subpixel value by OpenGL.
    return vec4(binary32)/255.;
}
于 2016-06-26T14:54:36.680 回答
2

我正在分享一个片段着色器示例,我实际上是如何调试的。

#version 410 core

uniform sampler2D samp;
in VS_OUT
{
    vec4 color;
    vec2 texcoord;
} fs_in;

out vec4 color;

void main(void)
{
    vec4 sampColor;
    if( texture2D(samp, fs_in.texcoord).x > 0.8f)  //Check if Color contains red
        sampColor = vec4(1.0f, 1.0f, 1.0f, 1.0f);  //If yes, set it to white
    else
        sampColor = texture2D(samp, fs_in.texcoord); //else sample from original
    color = sampColor;

}

在此处输入图像描述

于 2014-10-15T09:11:58.980 回答
1

现有的答案都是好东西,但我想再分享一个小宝石,它对于调试 GLSL 着色器中棘手的精度问题很有价值。将非常大的 int 数字表示为浮点数,需要注意正确使用 floor(n) 和 floor(n + 0.5) 以将 round() 实现为精确的 int。然后,可以通过以下逻辑将字节组件打包为 R、G 和 B 输出值,从而呈现一个精确的 int 浮点值。

  // Break components out of 24 bit float with rounded int value
  // scaledWOB = (offset >> 8) & 0xFFFF
  float scaledWOB = floor(offset / 256.0);
  // c2 = (scaledWOB >> 8) & 0xFF
  float c2 = floor(scaledWOB / 256.0);
  // c0 = offset - (scaledWOB << 8)
  float c0 = offset - floor(scaledWOB * 256.0);
  // c1 = scaledWOB - (c2 << 8)
  float c1 = scaledWOB - floor(c2 * 256.0);

  // Normalize to byte range
  vec4 pix;  
  pix.r = c0 / 255.0;
  pix.g = c1 / 255.0;
  pix.b = c2 / 255.0;
  pix.a = 1.0;
  gl_FragColor = pix;
于 2016-05-17T18:33:21.083 回答
0

GLSL Shader源代码由图形驱动程序编译和链接,并在 GPU 上执行。
如果要调试着色器,则必须使用RenderDocNVIDIA Nsight等图形调试器。

于 2020-07-25T13:21:07.277 回答
0

我找到了一个非常好的 github 库 ( https://github.com/msqrt/shader-printf ) 您可以在着色器文件中使用 printf 函数。

于 2021-07-21T13:26:09.500 回答
0

起诉这个

vec3 dd(vec3 finalColor,vec3 valueToDebug){
            //debugging
            finalColor.x = (v_uv.y < 0.3 && v_uv.x < 0.3) ? valueToDebug.x : finalColor.x;
            finalColor.y = (v_uv.y < 0.3 && v_uv.x < 0.3) ? valueToDebug.y : finalColor.y;
            finalColor.z = (v_uv.y < 0.3 && v_uv.x < 0.3) ? valueToDebug.z : finalColor.z;

            return finalColor;
        }

//on the main function, second argument is the value to debug
colour = dd(colour,vec3(0.0,1.0,1.));

gl_FragColor = vec4(clamp(colour * 20., 0., 1.),1.0);
于 2021-11-06T23:35:57.293 回答
-3

对纹理进行离线渲染并评估纹理的数据。您可以通过谷歌搜索“渲染到纹理”opengl 找到相关代码,然后使用 glReadPixels 将输出读入数组并对其执行断言(因为在调试器中查看如此巨大的数组通常不是很有用)。

此外,您可能希望禁用钳制以输出不在 0 和 1 之间的值,这仅支持浮点纹理

我个人被正确调试着色器的问题困扰了一段时间。似乎没有一个好方法 - 如果有人找到一个好的(而不是过时/弃用的)调试器,请告诉我。

于 2011-01-01T16:45:03.097 回答