GLSL 쉐이더를 디버깅하는 방법?


193

GLSL 프로그램을 디버깅해야하지만 중간 결과를 출력하는 방법을 모르겠습니다. GLSL을 사용하여 printf와 같은 디버그 추적을 만들 수 있습니까?


6
... glslDevil과 같은 외부 소프트웨어를 사용하지 않습니다.
Franck Freiburger 15시 13 분

GLSL Fragment 쉐이더에서 플로트 변수와 텍스트의 디버그 인쇄를 살펴보십시오. 인쇄 된 영역에서 출력 값의 글꼴과 일정한 상태를 위해 단일 예비 텍스처 단위가 필요합니다
Spektre

답변:


118

GLSL 내에서 CPU와 쉽게 다시 통신 할 수 없습니다. glslDevil 또는 다른 도구를 사용하는 것이 가장 좋습니다.

printf는 GLSL 코드를 실행하는 GPU에서 CPU로 돌아 가려고합니다. 대신 디스플레이로 넘어갈 수 있습니다. 텍스트를 출력하는 대신 시각적으로 독특한 것을 화면에 출력하십시오. 예를 들어 printf를 추가하려는 코드의 지점에 도달 한 경우에만 특정 색상으로 무언가를 페인트 할 수 있습니다. 값을 인쇄해야하는 경우 해당 값에 따라 색상을 설정할 수 있습니다.


62
셰이더를 디버깅하려는 정확한 이유가 화면에 아무 것도 나타나지 않기 때문에 어떻게해야합니까?
Jeroen

11
왜 아무것도 디버깅하고 싶습니까? 코드와 그가 런타임 값을 검사하기를 원하기 때문에 위험 할 것입니다 ....
RichieHH

3
GLSL-Debugger 는 glslDevil의 오픈 소스 포크입니다.
Magnus

@ Magnus는 더 이상 적극적으로 유지 관리되지 않으며 GLSL을 최대 1.20까지만 지원합니다.
Ruslan

57
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;
}

8
디버깅 장치입니다. 예를 들어 장면에서 빛의 위치를 ​​알고 싶다면 if (lpos.x> 100) bug = 1.0으로 이동하십시오. 조명 위치가 100보다 크면 장면이 빨간색으로 바뀝니다.
ste3e

13

Transform Feedback 이 버텍스 쉐이더 디버깅에 유용한 도구 라는 것을 알았습니다 . 이를 사용하여 VS 출력 값을 캡처하고 래스터 라이저를 거치지 않고도 CPU 측에서 다시 읽을 수 있습니다.

다음 은 변환 피드백에 대한 자습서에 대한 또 다른 링크입니다.


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);

픽셀마다 어떻게 다른지 알 수 있습니다.

여기에 이미지 설명을 입력하십시오

물론 원하는 색상 세트를 사용할 수 있습니다.


7

GLSL 샌드 박스 는 셰이더에 매우 유용했습니다.

디버깅 자체는 불가능하지만 (답변 할 수없는 것으로 답변 됨) 출력의 변경 사항을 빠르게 확인할 수 있습니다.


5

https://github.com/msqrt/shader-printf 를 사용해보십시오 . "GLSL의 간단한 printf 기능"이라고하는 적절한 구현입니다.

또한 ShaderToy를 사용 해보고 싶을 수도 있습니다. "The Art of Code"YouTube 채널 의 비디오 ( https://youtu.be/EBrAdahFtuo )에서 디버깅 및 시각화. 그는 정말 좋은 자료를 쓸 때 채널을 강력하게 추천 할 수 있으며 복잡한 아이디어를 소설적이고 매력적이며 소화하기 쉬운 형식으로 제시 할 수 있습니다 (Mandelbrot 비디오는 정확히 그 훌륭한 예입니다 : https : // youtu.be/6IWXkV82oyY )

나는이 늦은 답변을 신경 쓰지 않기를 희망하지만 GLSL 디버깅에 대한 Google 검색에서 질문의 순위가 높으며 9 년 만에 많은 변화가있었습니다 :-)

PS : 다른 대안으로는 NVIDIA nSight 및 AMD ShaderAnalyzer가 포함될 수 있으며 쉐이더를위한 전체 스테핑 디버거를 제공합니다.


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;

}

여기에 이미지 설명을 입력하십시오


2

이 답변의 맨 아래에는 floatIEEE 754를 인코딩 하여 전체 값을 컬러 로 출력 할 수있는 GLSL 코드의 예가 있습니다 binary32. 다음과 같이 사용합니다 (이 코드 조각은 yymodelview 매트릭스의 구성 요소를 제공합니다 ).

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 의 16 진수 표현을 얻을 수 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.;
}

1

텍스처에 대한 오프라인 렌더링을 수행하고 텍스처의 데이터를 평가하십시오. "렌더링하여 텍스처링하기"opengl에 대한 인터넷 검색을 통해 관련 코드를 찾을 수 있습니다. 그런 다음 glReadPixels를 사용하여 출력을 배열로 읽고 어설 션을 수행합니다 (디버거에서 이러한 거대한 배열을 살펴 보는 것은 실제로 유용하지 않기 때문에).

또한 부동 소수점 텍스처 에만 지원되는 0과 1 사이의 값을 출력하지 않도록 클램핑을 비활성화 할 수 있습니다 .

나는 개인적으로 한동안 쉐이더를 올바르게 디버깅하는 문제로 귀찮았습니다. 좋은 방법이없는 것 같습니다-누군가가 좋은 (그리고 구식이거나 더 이상 사용되지 않는) 디버거를 찾으면 알려주십시오.


3
"google xyz"라는 답변이나 의견은 Stackoverflow에서 금지 또는 투표 거부해야합니다.
gregoiregentil

1

기존 답변은 모두 좋은 것이지만 GLSL 셰이더에서 까다로운 정밀도 문제를 디버깅하는 데 유용한 작은 보석을 하나 더 공유하고 싶었습니다. 부동 소수점으로 표현 된 매우 큰 int 숫자를 사용하면 round ()를 정확한 int로 구현하기 위해 floor (n) 및 floor (n + 0.5)를 올바르게 사용해야합니다. 그런 다음 바이트 구성 요소를 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;
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.