2010-03-24 283 views
150

我需要調試一個GLSL程序,但我不知道如何輸出中間結果。 是否可以使用GLSL進行一些調試跟蹤(如使用printf)?如何調試GLSL着色器?

+6

...無需使用外部軟件,如glslDevil。 – 2010-03-24 15:13:06

+0

看看這個[從GLSL碎片着色器調試浮點變量和文本的打印](https://stackoverflow.com/a/44797902/2521214)你只需要單個備用紋理單元用於輸出值的字體和常量狀態打印區域 – Spektre 2017-06-29 07:55:30

回答

90

您不能輕鬆地從GLSL內部傳回CPU。使用glslDevil或其他工具是你最好的選擇。

printf將需要嘗試從運行GLSL代碼的GPU返回CPU。相反,您可以嘗試推進顯示。不要嘗試輸出文本,而是輸出視覺上與屏幕不同的內容。例如,只有在達到要添加printf的代碼點時纔可以繪製特定顏色。如果您需要打印一個值,您可以根據該值設置顏色。

+24

如果您想調試着色器的確切原因是什麼,是因爲屏幕上沒有顯示任何內容? – 2015-07-02 21:42:12

+5

你爲什麼要調試任何東西?因爲它的代碼和他想檢查運行時間值,我會冒險.... – RichieHH 2015-08-31 10:57:07

+3

[GLSL-Debugger](http://glsl-debugger.github.io/)是glslDevil的開源分支。 – Magnus 2016-09-10 12:24:00

2

對紋理進行離線渲染並評估紋理的數據。 您可以通過搜索「渲染到紋理」來查找相關的代碼。然後使用glReadPixels將輸出讀入數組並在其上執行斷言(因爲在調試器中查看如此巨大的數組通常不是很有用)。

此外,您可能希望禁用鉗位,以輸出不在0和1之間的值,該值僅在floating point textures受支持。

我個人一直在糾正着色器的問題。似乎沒有什麼好方法 - 如果任何人發現一個好的(並且不是過時的/不贊成的)調試器,請告訴我。

+1

任何回答或評論說「谷歌xyz」應該被禁止或從Stackoverflow投票。 – gregoiregentil 2016-08-27 00:23:57

48
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; 
} 
+7

這是一個調試設備。例如,如果您想知道場景中的燈光位置,請執行:if(lpos.x> 100)bug = 1.0。如果燈光位置大於100,場景將變爲紅色。 – ste3e 2011-10-15 08:39:22

+0

這是一個很棒的伎倆! ;)謝謝斯蒂芬! – sinner 2012-12-28 21:38:41

12

我發現Transform Feedback是調試頂點着色器的有用工具。您可以使用它來捕獲VS輸出的​​值,並在CPU端讀取它們,而無需通過光柵化器。

Here是轉換反饋教程的另一個鏈接。

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; 

} 

enter image description here

7

如果你希望顯示在屏幕上的值的變化,可以使用類似這樣的熱圖功能(我寫的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); 

並能得到它在你的像素是如何變化的一個想法:

enter image description here

當然你可以使用任何你喜歡的顏色集合。

4

GLSL Sandbox對於着色器來說我已經非常方便了。

本身沒有進行調試(已被回答爲無能力),但很方便快速查看輸出的變化。

1

現有的答案都是很好的東西,但我想分享一個在調試GLSL着色器中棘手的精度問題方面有價值的小寶石。將非常大的int數表示爲浮點數時,需要注意正確地使用floor(n)和floor(n + 0.5)來實現round()。然後可以通過以下邏輯呈現一個精確int的浮點值,以將字節分量打包爲R,G和B輸出值。

// 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; 
1

在此答案的底部是GLSL代碼的例子,其允許輸出完整float值作爲顏色編碼IEEE 754 binary32。我用它喜歡如下(本段給出了模型視圖矩陣的yy分量):

你在屏幕上之後,你可以採取任何顏色選擇器,格式化顏色爲HTML(附加00rgb值,如果你不需要更高的精度,並且如果你這樣做了第二遍以得到低字節),並且你得到作爲IEEE 754 binary32float的十六進制表示。

下面是實際執行的toColor()

#version 120 

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=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 int 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(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 binary32/255.; 
}