2015-02-17 1 views
1

Я создаю внутреннюю тень (ту же идею, что и внутреннее свечение, но темное вместо света), вычисляя ближайшее расстояние до края для каждого пикселя. Это делается в шейдере фрагмента путем интерполяции положения вершин из вершинного шейдера (с использованием varying), прохождения граничных точек формы через равномерный массив и вычисления ближайшего расстояния путем вычисления расстояния между точками от каждого сегмента к каждому край.Почему есть светлая область между краями при визуализации внутренней тени путем вычисления ближайшего расстояния до края?

Вот фрагмент шейдера:

precision mediump float; 
uniform mediump sampler2D uBaseSampler; 
uniform mediump vec2 uVerts[11]; 
varying mediump vec2 vTexCoord; 
varying mediump vec4 vPosition; 
float distanceSq(mediump vec2 a, mediump vec2 b) 
{ 
    mediump vec2 c = b - a; 
    return dot(c, c); 
} 
float distSqFromLineSegment(mediump vec2 p, mediump vec2 a, mediump vec2 b) 
{ 
    float lenSq = distanceSq(a, b); 
    /*if (lenSq == 0.0) return distanceSq(p, a);*/ 
    float t = dot(p - a, b - a)/lenSq; 
    if (t < 0.0) return distanceSq(p, a); 
    else if (t > 1.0) return distanceSq(p, b); 
    mediump vec2 proj = a + t * (b - a); 
    return distanceSq(p, proj); 
} 
void main() 
{ 
    float nearestDistSq = 1e3; 
    for (lowp int i = 0; i < 10; ++i) 
    { 
     lowp int j = i + 1; 
     float distSq = distSqFromLineSegment(vPosition.xy, uVerts[i], uVerts[j]); 
     nearestDistSq = min(nearestDistSq, sqrt(distSq)); 
    } 
    float nearestDist = nearestDistSq; 
    float coef = nearestDist; 
    lowp vec4 frag = texture2D(uBaseSampler, vTexCoord); 
    gl_FragColor = vec4(frag.xyz * coef, 1.0); 
} 

Вот как это выглядит при расчете внутренней тени на основе только края 1:

Inner shadow from edge 1

Вот как это выглядит при расчете внутренней тени на основе только на кромке 2:

Inner shadow from edge 2

Обратите внимание, как внутренняя тень правильна для обоих краев отдельно.

Теперь, вот как это выглядит при расчете внутренней тени, основанной на обоих краях:

Inner shadow from edges 1 and 2

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

Inner shadow from all edges

При взгляде на внутренней тени, порожденной краем 1 и кромкой 2 независимо друг от друга, это не делает похоже, что внутренняя легкость будет формироваться путем факторизации обоих ребер одновременно. Что может быть причиной? Точки вдоль легкости не должны иметь большее расстояние, чем их соседние точки, потому что я рассчитываю минимальное расстояние от всех краев.

Любые мысли?

Update:

на основе некоторых ранних ответов, это выглядит, как мой вопрос, что я реализовал «затемнить» алгоритм, и объединение двух градиентов таким образом вызывает побочный эффект в скриншотах. Например, если вы возьмете первые два снимка экрана, нанесите их в редактор изображений и установите второй слой в режим «затемнения», вы получите третий скриншот.

Что я действительно ищу, это своего рода умножение. Если я использую режим смешивания «умножать» в моем редакторе изображений, я получаю желаемый вид:

Inner shadow from edges 1 and 2 multiplied

Как бы я этого добиться? Похоже, что я должен вычислить внутреннюю тень независимо для каждого края, а затем умножить все коэффициенты вместе, вместо вычисления одного коэффициента на основе ближайшего края.

Обновление 2:

Реализация мультипликативного режим приводит смесь в темных областях вокруг вершин формы. Оказалось хуже, чем моя оригинальная версия. Я решил придерживаться оригинальной версии, так как это быстро, правильно, и нежелательные побочные эффекты сводятся к минимуму, уменьшая коэффициент около 0.8-1.0 вместо 0.0-1.0. Я также принял ответ, который привел меня по этому пути.

ответ

2

Нет ничего плохого в картине ... OpenGL рисует именно то, что вы просили, что является яркостью, связанной с расстоянием от края. Расстояние не будет гладкой функцией, поэтому ваш глаз видит края, где производная от функции расстояния внезапно изменяется.

Вы можете проверить это самостоятельно. Откройте программу редактирования изображений, такую ​​как Gimp или Photoshop или что-то еще, и сделайте два линейных градиента. Для второго градиента выберите «затемнить» в качестве режима композиции для градиента. Я получил следующее изображение:

Gradient image

Вы можете видеть, что есть подобная линия 45 градусов здесь.

Если вы хотите избавиться от острых краев, вам нужно будет сделать что-то еще, например, размыть результат. Расстояние полей в целом будет иметь острые края.

+0

Это не помогло мне достичь желаемого результата, но это очень помогло мне понять, почему я получаю результаты, которые получаю. Вы совершенно правы в том, что я в основном реализовал неправильный режим смешивания - похоже, мне нужно реализовать мультипликативный режим наложения. Я обновил свой вопрос с помощью этой дополнительной информации. Благодаря! – Agop

+0

Итак, я закончил реализацию мультипликативного режима наложения, и на самом деле это выглядит довольно плохо. Он вызывает более темные круговые области вокруг вершин формы, потому что тени от двух ребер умножаются. Тем не менее, он выглядит лучше в складках. Я буду отмечать ваш ответ как правильный, поскольку вы совершенно правы, ничего не случилось с моим оригинальным алгоритмом. – Agop

+0

Вы всегда можете использовать размытие, что является довольно традиционным способом делать вещи. (1) сделать фигуру текстурой (2) размыть текстуру с помощью вашего шейдера фрагмента. –