2016-08-16 39 views
4

進行比較我有一個webgl的模糊着色器:WebGL的:循環索引不能與非恆定表達

precision mediump float; 
precision mediump int; 

uniform sampler2D u_image; 
uniform float blur;  
uniform int u_horizontalpass; // 0 or 1 to indicate vertical or horizontal pass 
uniform float sigma;  // The sigma value for the gaussian function: higher value means more blur 
          // A good value for 9x9 is around 3 to 5 
          // A good value for 7x7 is around 2.5 to 4 
          // A good value for 5x5 is around 2 to 3.5 
          // ... play around with this based on what you need :) 

varying vec4 v_texCoord; 

const vec2 texOffset = vec2(1.0, 1.0); 
// uniform vec2 texOffset; 
const float PI = 3.14159265; 

void main() { 
    vec2 p = v_texCoord.st; 
    float numBlurPixelsPerSide = blur/2.0; 

    // Incremental Gaussian Coefficent Calculation (See GPU Gems 3 pp. 877 - 889) 
    vec3 incrementalGaussian; 
    incrementalGaussian.x = 1.0/(sqrt(2.0 * PI) * sigma); 
    incrementalGaussian.y = exp(-0.5/(sigma * sigma)); 
    incrementalGaussian.z = incrementalGaussian.y * incrementalGaussian.y; 

    vec4 avgValue = vec4(0.0, 0.0, 0.0, 0.0); 
    float coefficientSum = 0.0; 

    // Take the central sample first... 
    avgValue += texture2D(u_image, p) * incrementalGaussian.x; 
    coefficientSum += incrementalGaussian.x; 
    incrementalGaussian.xy *= incrementalGaussian.yz; 

    // Go through the remaining 8 vertical samples (4 on each side of the center) 
    for (float i = 1.0; i <= numBlurPixelsPerSide; i += 1.0) { 
    avgValue += texture2D(u_image, p - i * texOffset) * incrementalGaussian.x;   
    avgValue += texture2D(u_image, p + i * texOffset) * incrementalGaussian.x;   
    coefficientSum += 2.0 * incrementalGaussian.x; 
    incrementalGaussian.xy *= incrementalGaussian.yz; 
    } 

    gl_FragColor = avgValue/coefficientSum; 
} 

當我建立時,得到以下錯誤消息:

webgl-renderer.js?2eb3:137 Uncaught could not compile shader:ERROR: 0:38: 'i' : Loop index cannot be compared with non-constant expression

我有也試圖用只是統一的浮點模糊來比較我。有沒有什麼辦法解決這一問題?

的問題是更詳細的在這裏:https://www.khronos.org/webgl/public-mailing-list/archives/1012/msg00063.php

的解決方案,我發現東張西望是隻使用一個常量表達式比較循環變種時。這不符合我需要做的事情,這是根據模糊半徑循環的次數。

對此有何看法?

回答

3

嘗試這樣:

const float MAX_ITERATIONS = 100.0; 

// Go through the remaining 8 vertical samples (4 on each side of the center) 
for (float i = 1.0; i <= MAX_ITERATIONS; i += 1.0) { 
    if (i >= numBlurPixelsPerSide){break;} 
    avgValue += texture2D(u_image, p - i * texOffset) * incrementalGaussian.x;   
    avgValue += texture2D(u_image, p + i * texOffset) * incrementalGaussian.x;   
    coefficientSum += 2.0 * incrementalGaussian.x; 
    incrementalGaussian.xy *= incrementalGaussian.yz; 
} 
0

我已經與圖像採樣着色器類似的問題。該代碼基本上是一樣的:

for (int dx = -2 * SCALE_FACTOR; dx < 2 * SCALE_FACTOR; dx += 2) { 
    for (int dy = -2 * SCALE_FACTOR; dy < 2 * SCALE_FACTOR; dy += 2) { 
     /* accumulate fragment's color */ 
    } 
} 

我已經結束了有什麼用預處理和創造使用的每個SCALE_FACTOR(幸運的是,只有4個需要)單獨着色器程序做。爲了實現這一目標,一個小的輔助函數的實施是爲了#define ...語句添加到shader代碼:

function insertDefines (shaderCode, defines) { 
    var defineString = ''; 

    for (var define in defines) { 
     if (defines.hasOwnProperty(define)) { 
      defineString += 
       '#define ' + define + ' ' + defines[define] + '\n'; 
     } 
    } 

    var versionIdx = shaderCode.indexOf('#version'); 

    if (versionIdx == -1) { 
     return defineString + shaderCode; 
    } 

    var nextLineIdx = shaderCode.indexOf('\n', versionIdx) + 1; 

    return shaderCode.slice(0, nextLineIdx) + 
     defineString + 
     shaderCode.slice(nextLineIdx); 
} 

的實施是有點棘手,因爲如果代碼中已經有#version預處理語句,所有其他的語句必須遵循它。

然後我添加了被定義爲SCALE_FACROR檢查:

#ifndef SCALE_FACTOR 
# error SCALE_FACTOR is undefined 
#endif 

而在我的javascript代碼我做了這樣的事情:

var SCALE_FACTORS = [4, 8, 16, 32], 
    shaderCode, // the code of my shader 
    shaderPrograms = SCALE_FACTORS.map(function (factor) { 
     var codeWithDefines = insertDefines(shaderCode, { SCALE_FACTOR: factor }); 
     /* compile shaders, link program, return */ 
    }); 
4

這是因爲在某些硬件上, GLSL循環被解壓縮爲本地GPU指令。這意味着需要對通過for循環的傳遞數量設置一個硬性上限,該循環決定了將生成多少個循環內部代碼副本。如果用const float或者#define指令替換numBlurPixelsPerSide,然後着色器編譯器可以在編譯時確定遍數,並相應地生成代碼。但是在那裏統一,在編譯時不知道上限。

有沒有在這條規則的有趣的皺紋:你被允許break或撥打return早出for循環,即使最大的迭代必須辨別的編譯時間。例如,考慮this tiny Mandelbrot shader。這很難說是對GLSL Sandbox中最漂亮的分形,但我選擇了它,爲它的小尺寸:

precision mediump float; 
uniform float time; 
uniform vec2 mouse; 
uniform vec2 resolution; 
varying vec2 surfacePosition; 

const float max_its = 100.; 

float mandelbrot(vec2 z){ 
    vec2 c = z; 
    for(float i=0.;i<max_its;i++){  // for loop is here. 
     if(dot(z,z)>4.) return i;  // conditional early return here. 
     z = vec2(z.x*z.x-z.y*z.y,2.*z.x*z.y)+c; 
    } 
    return max_its; 
} 


void main(void) { 
    vec2 p = surfacePosition; 
    gl_FragColor = vec4(mandelbrot(p)/max_its); 
} 

在這個例子中,max_itsconst所以編譯器知道上限和可以取消滾動這個循環,如果它需要。在循環內部,return語句爲Mandelbrot集外的像素提供了一個離開循環的方法。

您仍然不希望將最大迭代設置得太高,因爲這可能會產生大量GPU指令,並可能損害性能。

0

我使用OpenGL ES3在Android和使用程序是這樣開始上述擴展解決這個問題:

#extension GL_EXT_gpu_shader5 : require 

,我不知道是否對WebGL的工作,但你可以試試。 希望它可以幫助。