Глубина резкости: комбинирование точечного шейдера с шейдером размытия (обработка 3) - PullRequest
0 голосов
/ 05 июня 2018

Я хотел бы отобразить тысячи точек на трехмерном холсте (в обработке) с эффектом глубины резкости.Более конкретно, я хотел бы использовать z-буфер (буферизацию глубины) для регулировки уровня размытия point в зависимости от его расстояния от камеры.

Пока что я мог бы придуматьследующий точечный шейдер:

pointfrag.glsl

#ifdef GL_ES
precision mediump float;
precision mediump int;
#endif

varying vec4 vertColor;
uniform float maxDepth;

void main() {

  float depth = gl_FragCoord.z / gl_FragCoord.w;
  gl_FragColor = vec4(vec3(vertColor - depth/maxDepth), 1) ;

}

pointvert.glsl

uniform mat4 projection;
uniform mat4 modelview;

attribute vec4 position;
attribute vec4 color;
attribute vec2 offset;


varying vec4 vertColor;
varying vec4 vertTexCoord;

void main() {
  vec4 pos = modelview * position;
  vec4 clip = projection * pos;

  gl_Position = clip + projection * vec4(offset, 0, 0);

  vertColor = color;
}

У меня также естьразмытие шейдера (первоначально из библиотеки PostFX ):

blurfrag.glsl

#ifdef GL_ES
precision mediump float;
precision mediump int;
#endif


#define PROCESSING_TEXTURE_SHADER

uniform sampler2D texture;

// The inverse of the texture dimensions along X and Y
uniform vec2 texOffset;

varying vec4 vertColor;
varying vec4 vertTexCoord;

uniform int blurSize;       
uniform int 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 <span class="Emoticon Emoticon1"><span>:)</span></span>

const float pi = 3.14159265;

void main() {  
  float numBlurPixelsPerSide = float(blurSize / 2); 

  vec2 blurMultiplyVec = 0 < horizontalPass ? vec2(1.0, 0.0) : vec2(0.0, 1.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(texture, vertTexCoord.st) * 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++) { 
    avgValue += texture2D(texture, vertTexCoord.st - i * texOffset * 
                          blurMultiplyVec) * incrementalGaussian.x;         
    avgValue += texture2D(texture, vertTexCoord.st + i * texOffset * 
                          blurMultiplyVec) * incrementalGaussian.x;         
    coefficientSum += 2.0 * incrementalGaussian.x;
    incrementalGaussian.xy *= incrementalGaussian.yz;
  }

  gl_FragColor = (avgValue / coefficientSum);
}

Вопрос :

  • Как объединить шейдер фрагмента размытия с шейдером точечного фрагмента?

В идеале мне бы хотелось иметь один фрагментный шейдер, который вычисляет уровень размытия на основе z-координаты точки.Это вообще возможно?

Любая помощь будет принята с благодарностью.


Пример эскиза с отображением точек с использованием pointfrag.glsl и pointvert.glsl шейдеры выше:

sketch.pde (необходим режим Python + библиотека PeasyCam)

add_library('peasycam')
liste = []

def setup():
    global pointShader, cam
    size(900, 900, P3D)
    frameRate(1000)
    smooth(8)

    cam = PeasyCam(this, 500)
    cam.setMaximumDistance(width)
    perspective(60 * DEG_TO_RAD, width/float(height), 2, 6000)

    pointShader = loadShader("pointfrag.glsl", "pointvert.glsl")
    pointShader.set("maxDepth", cam.getDistance()*3)

    for e in range(3000): liste.append(PVector(random(width), random(width), random(width)))

    shader(pointShader, POINTS)
    strokeWeight(2)
    stroke(255)

def draw():

    background(0)
    translate(-width/2, -width/2, -width/2)    
    for e in liste:
        point(e.x, e.y, e.z)

    cam.rotateY(.0002)
    cam.rotateX(.0001)

enter image description here

Ответы [ 2 ]

0 голосов
/ 12 июня 2018

Я был готов попробовать этот простой трюк, который, кажется, на какое-то время охватывает ваш случай использования :) Я реализовал в Unity, но логика довольно проста и должна легко адаптироваться.

pretty pretty

Если у вас нет других примитивов, кроме точек, вы можете легко преконвертировать размытие в текстуру.Я сделал мой в Ps, но я уверен, что есть более точные способы.Фото-шум не обязателен

texture of blur steps

Отсюда просто вычисляем смещение текстуры в зависимости от степени размытия (или, точнее, шагов n и n-1 и lerp, используя напоминание).В единстве я отобразил положение Z в пространстве вида и простое зеркальное линейное затухание (я не уверен, какова здесь реальная логика в оптике).(_ProjectionParams.w - обратная дальняя плоскость)

half focus = -UnityObjectToViewPos( v.vertex ).z;
focus = abs(focus - _FocalPlane);
focus *= _Dof * _ProjectionParams.w;

редактировать: я должен упомянуть, что я видел идею в демоверсии этой компании , однако они могли бы реализовать ее по-другому, я полагаюне знаю

0 голосов
/ 09 июня 2018

Основная проблема в вашей задаче состоит в том, что гауссовский размытый шейдер обычно работает в пост-процессе.Обычно он применяется ко всему окну просмотра после того, как вся геометрия была нарисована.Газовый шейдер размытия берет фрагмент кадрового буфера (текстуры) и его соседей, смешивает там цвет с помощью гауссовой функции и сохраняет новый цвет в конечном кадровом буфере.Для этого рисование всей сцены (всех точек) должно быть закончено раньше.


Но вы можете сделать что-то еще.Напишите шейдер, который рисует точки, полностью непрозрачные в его центре и полностью прозрачные на его внешней границе.

В вершинном шейдере вы должны передать координату вершины пространства просмотра и центр точки обзора в точкуфрагментный шейдер:

pointvert.glsl

uniform mat4 projection;
uniform mat4 modelview;

attribute vec4 position;
attribute vec4 color;
attribute vec2 offset;

varying vec3 vCenter;
varying vec3 vPos;
varying vec4 vColor;


void main() {

    vec4 center = modelview * position;
    vec4 pos    = center + vec4(offset, 0, 0); 

    gl_Position = projection * pos;

    vCenter = center.xyz;
    vPos    = pos.xyz;
    vColor  = color;
}

В фрагментном шейдере вы должны рассчитать расстояние от фрагмента до центра точки.Для этого вы должны знать размер точки.Расстояние можно использовать для вычисления opacity, а opacity - это новый альфа-канал точки.

Добавьте равномерную переменную strokeWeight и установите ее в программе.Обратите внимание, поскольку точки прозрачны на своих границах, они выглядят меньше.Я рекомендую увеличить размер точек:

pointShader.set("strokeWeight", 6.0)

.....

strokeWeight(6)

pointfrag.glsl

#ifdef GL_ES
precision mediump float;
precision mediump int;
#endif

varying vec3 vCenter;
varying vec3 vPos;
varying vec4 vColor;

uniform float strokeWeight;
uniform float maxDepth;
uniform float focus;

void main() {

    float depth = clamp(abs(vCenter.z)/maxDepth, 0.0, 1.0);
    float blur  = abs(focus-depth);

    float dist_to_center = length(vPos-vCenter)*2.0/strokeWeight;
    float threshold      = max(0.0, blur);
    float opacity        = 1.0 - smoothstep(threshold/2.0, 1.0-threshold/2.0, dist_to_center); 

    gl_FragColor = vec4(vColor.rgb, opacity);
}

Вы рисуете частично прозрачные объекты.Чтобы добиться правильного эффекта наложения, вы должны отсортировать точки по возрастающей координате z:

liste = []
listZ = []

.....

for e in range(3000): listZ.append(random(width))
listZ.sort()
for z in listZ: liste.append(PVector(random(width), random(width), z))

Полный пример кода может выглядеть следующим образом:

add_library('peasycam')
liste = []
listZ = []

def setup():
    global pointShader, cam
    size(900, 900, P3D)
    frameRate(1000)
    smooth(8)

    cam = PeasyCam(this, 500)
    cam.setMaximumDistance(width)
    perspective(60 * DEG_TO_RAD, width/float(height), 2, 6000)

    pointShader = loadShader("pointfrag.glsl", "pointvert.glsl")
    pointShader.set("maxDepth", 900.0)
    pointShader.set("strokeWeight", 6.0)

    for e in range(3000): listZ.append(random(width))
    listZ.sort()
    for z in listZ: liste.append(PVector(random(width), random(width), z))

    shader(pointShader, POINTS)
    strokeWeight(6)
    stroke(255)

def draw():

    background(0)
    blendMode(BLEND)
    translate(-width/2, -width/2, -width/2) 
    pointShader.set("focus", map(mouseX, 0, width, 0.2, 1.0))   
    for e in liste:
        point(e.x, e.y, e.z)

    cam.rotateY(.0002)
    cam.rotateX(.0001)

См. Предварительный просмотр:

preview 1


Конечно, можно также использовать гауссовский размывающий шейдер.

Гауссовский размывающий шейдер, который вы представляете в своемвопрос, это 2-х проходной шейдер размытия после процесса.Это означает, что он должен быть применен за 2 прохода после обработки для всего окна просмотра.Проход размывается по горизонтали, другой проход размывается по вертикальной оси.

Для этого вам необходимо выполнить следующие шаги:

  1. Отрисовать сцену в буфер (изображение)
  2. Примените проход вертикального размытия по Гауссу к изображению и отредактируйте результат в новый буфер изображения
  3. Примените проход по горизонтальному гауссовому размытию к результату прохода вертикального размытия по Гауссу

Листинг кода, в котором используются именно те шейдеры из вашего вопроса, может выглядеть так:

add_library('peasycam')
liste = []

def setup():
    global pointShader, blurShader, cam, bufScene, bufBlurV, bufBlurH
    size(900, 900, P3D)
    frameRate(1000)

    cam = PeasyCam(this, 900)
    cam.setMaximumDistance(width)
    perspective(60 * DEG_TO_RAD, width/float(height), 2, 6000)

    pointShader = loadShader("pointfrag.glsl", "pointvert.glsl")
    pointShader.set("maxDepth", cam.getDistance()*3)

    blurShader = loadShader("blurfrag.glsl")
    blurShader.set("texOffset", [1.0/width, 1.0/height])
    blurShader.set("blurSize", 40);
    blurShader.set("sigma", 5.0);

    bufScene, bufBlurV, bufBlurH  = [createGraphics(width, height, P3D) for e in range(3)]
    bufScene.smooth(8), bufBlurV.shader(blurShader), bufBlurH.shader(blurShader)

    for e in range(5000): liste.append(PVector(random(width), random(width), random(width)))

def drawScene(pg):
    pg.beginDraw()
    pg.background(0)

    shader(pointShader, POINTS)
    strokeWeight(4)
    stroke(255)

    pushMatrix()
    translate(-width/2, -width/2, 0.0)
    for e in liste:
        point(e.x, e.y, e.z)
    popMatrix()

    pg.endDraw()
    cam.getState().apply(pg)

def draw():
    drawScene(bufScene) 

    bufBlurV.beginDraw()
    blurShader.set("horizontalPass", 0);
    bufBlurV.image(bufScene, 0, 0)
    bufBlurV.endDraw()

    bufBlurH.beginDraw()
    blurShader.set("horizontalPass", 1);
    bufBlurH.image(bufBlurV, 0, 0)
    bufBlurH.endDraw()

    cam.beginHUD()
    image(bufBlurH, 0, 0)
    cam.endHUD()

    cam.rotateY(.0002)
    cam.rotateX(.0001)

См. Превью:

preview 2


Для подхода, который объединяет 2 решения, см. Также ответ на ваш предыдущий вопрос: Шейдер глубины резкости для точек / штрихов в обработке

Создайте шейдер глубины:

deep_vert.glsl

uniform mat4 projection;
uniform mat4 modelview;

attribute vec4 position;
attribute vec2 offset;

varying vec3 vCenter;

void main() {
    vec4 center = modelview * position;
    gl_Position = projection * (center + vec4(offset, 0, 0));
    vCenter = center.xyz;
}

deep_frag.glsl

#ifdef GL_ES
precision mediump float;
precision mediump int;
#endif

varying vec3 vCenter;

uniform float maxDepth;

void main() {
    float depth = clamp(abs(vCenter.z)/maxDepth, 0.0, 1.0);
    gl_FragColor = vec4(vec3(depth), 1.0);
}

Далее точкадля рисования точек нужен шейдер:

point_vert.glsl

uniform mat4 projection;
uniform mat4 modelview;

attribute vec4 position;
attribute vec4 color;
attribute vec2 offset;

varying vec4 vColor;

void main() {
    vec4 pos = modelview * position;
    gl_Position = projection * (pos + vec4(offset, 0, 0));
    vColor = color;
}

point_frag.glsl

#ifdef GL_ES
precision mediump float;
precision mediump int;
#endif

varying vec4 vColor;

void main() {
    gl_FragColor = vec4(vColor.rgb, 1.0);
}

2-проходная глубина резкости, гауссовский размытый шейдер выглядит так:

blurfrag.glsl

uniform sampler2D tDepth;

uniform float focus;

const float pi = 3.14159265;

void main()
{  
    vec2 vUv = vertTexCoord.st;
    vec4 depth = texture2D( tDepth, vUv );
    float dofblur = abs( depth.x - focus );

    float numBlurPixelsPerSide = float(blurSize / 2) * dofblur; 
    float dofSigma = sigma; 

    vec2 blurMultiplyVec = 0 < horizontalPass ? vec2(1.0, 0.0) : vec2(0.0, 1.0);

    // Incremental Gaussian Coefficent Calculation (See GPU Gems 3 pp. 877 - 889)
    vec3 incrementalGaussian;
    incrementalGaussian.x = 1.0 / (sqrt(2.0 * pi) * dofSigma);
    incrementalGaussian.y = exp(-0.5 / (dofSigma * dofSigma));
    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(texture, vertTexCoord.st) * 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++) { 
        avgValue += texture2D(texture, vertTexCoord.st - i * texOffset * 
                            blurMultiplyVec) * incrementalGaussian.x;         
        avgValue += texture2D(texture, vertTexCoord.st + i * texOffset * 
                            blurMultiplyVec) * incrementalGaussian.x;         
        coefficientSum += 2.0 * incrementalGaussian.x;
        incrementalGaussian.xy *= incrementalGaussian.yz;
    }

    gl_FragColor = (avgValue / coefficientSum);
}

В программе вам нужно выполнить 4 этапа: 1. Отрисовать сцену в буфер (изображение) 2. Отобразить «глубину» в другой буфер изображения 3. Применитьвертикальный переход по гауссовому размытию к изображению и рендеринг результата в новый буфер изображения 4. Примените горизонтальный проход по гауссовому размытию к результату прохода по вертикальному гауссовому размытию

add_library('peasycam')
liste = []

def setup():
    global depthShader, point_shader, blurShader, cam, bufDepth, bufScene, bufBlurV, bufBlurH
    size(900, 900, P3D)
    frameRate(1000)

    cam = PeasyCam(this, 900)
    cam.setMaximumDistance(width)
    perspective(60 * DEG_TO_RAD, width/float(height), 2, 6000)

    point_shader = loadShader("point_frag.glsl","point_vert.glsl")
    depthShader = loadShader("depth_frag.glsl","depth_vert.glsl")
    blurShader = loadShader("blurfrag.glsl")

    bufDepth, bufScene, bufBlurV, bufBlurH = [createGraphics(width, height, P3D) for e in range(4)]
    bufDepth.smooth(8)
    bufScene.smooth(8)
    bufBlurV.shader(blurShader)
    bufBlurH.shader(blurShader)

    depthShader.set("maxDepth", 900.0)

    blurShader.set("tDepth", bufScene)
    blurShader.set("texOffset", [1.0/width, 1.0/height])
    blurShader.set("blurSize", 40)
    blurShader.set("sigma", 5.0)

    for e in range(3000): liste.append(PVector(random(width), random(width), random(width)))

def drawScene(pg,sh):
    pg.beginDraw()
    pg.background(0)

    shader(sh, POINTS)
    strokeWeight(6)
    stroke(255)

    pushMatrix()
    translate(-width/2, -width/2, 0.0)
    for e in liste:
        point(e.x, e.y, e.z)
    popMatrix()

    pg.endDraw()
    cam.getState().apply(pg)

def draw():
    drawScene(bufDepth, point_shader) 
    drawScene(bufScene, depthShader)

    blurShader.set("focus", map(mouseX, 0, width, .1, 1))

    bufBlurV.beginDraw()
    blurShader.set("horizontalPass", 0);
    bufBlurV.image(bufScene, 0, 0)
    bufBlurV.endDraw()

    bufBlurH.beginDraw()
    blurShader.set("horizontalPass", 1);
    bufBlurH.image(bufBlurV, 0, 0)
    bufBlurH.endDraw()

    cam.beginHUD()
    image(bufBlurH, 0, 0)
    cam.endHUD()

См. предварительный просмотр:

preview 3

...