[Первая часть]
Разобравшись с основами, в этой части статьи мы реализуем такие эффекты, как контуры объектов, bloom, SSAO, размытие, глубина резкости, пикселизация и другие.
uniform struct
{ vec4 diffuse
;
} p3d_Material;
out vec4 fragColor;
void main() {
vec3 diffuseColor = p3d_Material.diffuse.rgb;
fragColor = vec4(diffuseColor, 1);
}
// ...
uniform sampler2D materialDiffuseTexture;
// ...
vec2 texSize = textureSize(materialDiffuseTexture, 0).xy;
vec2 texCoord = gl_FragCoord.xy;
// ...
// ...
int separation = 1;
// ...
separation
можно настраивать в соответствии со своим вкусом. Чем больше разделение, тем толще края или линии. // ...
float threshold = 0;
// ...
vec4 mx = vec4(0);
vec4 mn = vec4(1);
int x = -1;
int y = -1;
for (int i = 0; i < 9; ++i) {
vec4 color =
texture
( materialDiffuseTexture
, (texCoord + (vec2(x, y) * separation)) / texSize
);
mx = max(color, mx);
mn = min(color, mn);
x += 1;
if (x >= 2) {
x = -1;
y += 1;
}
}
float alpha = ((mx.r + mx.g + mx.b) / 3) - ((mn.r + mn.g + mn.b) / 3);
if (alpha > threshold) { alpha = 1; }
// ...
// ...
vec3 lineRgb = vec3(0.012, 0.014, 0.022);
// ...
vec4 lineColor = vec4(lineRgb, alpha);
// ...
fragColor = lineColor;
// ...
// ...
float threshold = 0;
// ...
if (alpha > threshold) { alpha = 1; }
// ...
// ...
uniform struct p3d_FogParameters
{ vec4 color
; float start
; float end
;
} p3d_Fog;
// ...
// ...
float fogIntensity =
clamp
( ( p3d_Fog.end - vertexPosition.y)
/ ( p3d_Fog.end - p3d_Fog.start)
, 0
, 1
);
fogIntensity = 1 - fogIntensity;
// ...
fogIntensity
приближается к единице. Для всех вершин после конца тумана fogIntensity
ограничивается сверху значением 1. // ...
fragColor =
mix
( outputColor
, p3d_Fog.color
, fogIntensity
);
// ...
fogIntensity
к единице будет всё меньше outputColor
и всё больше цвета тумана. Когда fogIntensity
достигает единицы, останется только цвет тумана.// ...
uniform sampler2D positionTexture;
// ...
vec4 position = texture(positionTexture, texCoord / texSize);
float fogIntensity =
clamp
( ( p3d_Fog.end - position.y)
/ ( p3d_Fog.end - p3d_Fog.start)
, 0
, 1
);
fogIntensity = 1 - fogIntensity;
vec4 lineWithFogColor =
mix
( lineColor
, p3d_Fog.color
, fogIntensity
);
fragColor = vec4(lineWithFogColor.rgb, alpha);
// ...
positionTexture
— это текстура буфера кадров, которая содержит позиции вершин пространства обзора. Вы узнаете об этом, когда мы будем реализовывать шейдер SSAO. //...
float separation = 3;
int samples = 15;
float threshold = 0.5;
float amount = 1;
// ...
// ...
int size = samples;
int size2 = size * size;
int x = 0;
int y = 0;
// ...
float value = 0;
vec4 result = vec4(0);
vec4 color = vec4(0);
// ...
for (int i = 0; i < size2; ++i) {
// ...
}
// ...
samples
на samples
, центрированному относительно текущего фрагмента. Оно похоже на окно, использованное для создания контуров. // ...
color =
texture
( bloomTexture
, ( gl_FragCoord.xy
+ vec2(x * separation, y * separation)
)
/ texSize
);
value = ((0.3 * color.r) + (0.59 * color.g) + (0.11 * color.b));
if (value < threshold) { color = vec4(0); }
result += color;
// ...
result
. // ...
result = result / size2;
// ...
PT(Texture) depthTexture = new Texture("depthTexture");
depthTexture->set_format(Texture::Format::F_depth_component32);
PT(GraphicsOutput) depthBuffer = graphicsOutput->make_texture_buffer("depthBuffer", 0, 0, depthTexture);
depthBuffer->set_clear_color(LVecBase4f(0, 0, 0, 0));
NodePath depthCameraNP = window->make_camera();
DCAST(Camera, depthCameraNP.node())->set_lens(window->get_camera(0)->get_lens());
PT(DisplayRegion) depthBufferRegion = depthBuffer->make_display_region(0, 1, 0, 1);
depthBufferRegion->set_camera(depthCameraNP);
in vec4 vertexPosition;
out vec4 fragColor;
void main() {
fragColor = vertexPosition;
}
[0, 1]
, и чтобы у каждого была достаточно большая точность (достаточно большое количество бит). Например, если какая-то интерполированная позиция вершины равна <-139.444444566, 0.00000034343, 2.5>
, то нельзя сохранять её в текстуру как <0.0, 0.0, 1.0>
. // ...
FrameBufferProperties fbp = FrameBufferProperties::get_default();
// ...
fbp.set_rgba_bits(32, 32, 32, 32);
fbp.set_rgb_color(true);
fbp.set_float_color(true);
// ...
[0, 1]
. Вызов set_rgba_bits(32, 32, 32, 32)
задаёт битовый объём и отключает ограничение. glTexImage2D
( GL_TEXTURE_2D
, 0
, GL_RGB32F
, 1200
, 900
, 0
, GL_RGB
, GL_FLOAT
, nullptr
);
GL_RGB32F
задаёт биты и отключает ограничение.Если буфер цветов имеет фиксированную запятую, то компоненты исходных и конечных значений, а также показатели смешения перед вычислением уравнения смешивания ограничиваются значениями [0, 1] или [?1, 1], соответственно для беззнакового нормализованного и знакового нормализованного буфера цветов. Если буфер цветов имеет плавающую запятую, то ограничение не выполняется.
Источник
gl-coordinate-system default
.in vec3 vertexNormal;
out vec4 fragColor;
void main() {
vec3 normal = normalize(vertexNormal);
fragColor = vec4(normal, 1);
}
gl-coordinate-system default
. // ...
for (int i = 0; i < numberOfSamples; ++i) {
LVecBase3f sample =
LVecBase3f
( randomFloats(generator) * 2.0 - 1.0
, randomFloats(generator) * 2.0 - 1.0
, randomFloats(generator)
).normalized();
float rand = randomFloats(generator);
sample[0] *= rand;
sample[1] *= rand;
sample[2] *= rand;
float scale = (float) i / (float) numberOfSamples;
scale = lerp(0.1, 1.0, scale * scale);
sample[0] *= scale;
sample[1] *= scale;
sample[2] *= scale;
ssaoSamples.push_back(sample);
}
// ...
ssaoSamples
будут переданы в шейдер SSAO. LVecBase3f sample =
LVecBase3f
( randomFloats(generator) * 2.0 - 1.0
, randomFloats(generator) * 2.0 - 1.0
, randomFloats(generator) * 2.0 - 1.0
).normalized();
// ...
for (int i = 0; i < 16; ++i) {
LVecBase3f noise =
LVecBase3f
( randomFloats(generator) * 2.0 - 1.0
, randomFloats(generator) * 2.0 - 1.0
, 0.0
);
ssaoNoise.push_back(noise);
}
// ...
// ...
float radius = 1.1;
float bias = 0.026;
float lowerRange = -2;
float upperRange = 2;
// ...
[0, 1]
на любое выбранное вами значение. Увеличивая диапазон, можно повысить контрастность. // ...
vec4 position = texture(positionTexture, texCoord);
vec3 normal = texture(normalTexture, texCoord).xyz;
int noiseX = int(gl_FragCoord.x - 0.5) % 4;
int noiseY = int(gl_FragCoord.y - 0.5) % 4;
vec3 random = noise[noiseX + (noiseY * 4)];
// ...
// ...
vec3 tangent = normalize(random - normal * dot(random, normal));
vec3 binormal = cross(normal, tangent);
mat3 tbn = mat3(tangent, binormal, normal);
// ...
// ...
float occlusion = NUM_SAMPLES;
for (int i = 0; i < NUM_SAMPLES; ++i) {
// ...
}
// ...
// ...
vec3 sample = tbn * samples[i];
sample = position.xyz + sample * radius;
// ...
// ...
vec4 offset = vec4(sample, 1.0);
offset = lensProjection * offset;
offset.xyz /= offset.w;
offset.xyz = offset.xyz * 0.5 + 0.5;
// ...
-1 * 0.5 + 0.5 = 0
1 * 0.5 + 0.5 = 1
// ...
vec4 offsetPosition = texture(positionTexture, offset.xy);
float occluded = 0;
if (sample.y + bias <= offsetPosition.y) { occluded = 0; } else { occluded = 1; }
// ...
// ...
float intensity =
smoothstep
( 0.0
, 1.0
, radius
/ abs(position.y - offsetPosition.y)
);
occluded *= intensity;
occlusion -= occluded;
// ...
// ...
occlusion /= NUM_SAMPLES;
// ...
fragColor = vec4(vec3(occlusion), position.a);
// ...
[0, NUM_SAMPLES]
в интервал [0, 1]
. Ноль означает полную occlusion, единицы — отсутствие occlusion. Теперь назначим показатель occlusion цвету фрагмента, и на этом всё. // ...
for (int i = 0; i < size2; ++i) {
x = size - xCount;
y = yCount - size;
result +=
texture
( ssaoTexture
, texCoord
+ vec2(x * parameters.x, y * parameters.x)
).rgb;
xCount -= 1;
if (xCount < countMin) { xCount = countMax; yCount -= 1; }
}
result = result / size2;
// ...
parameters.x
— это параметр разделения. // ...
vec2 ssaoBlurTexSize = textureSize(ssaoBlurTexture, 0).xy;
vec2 ssaoBlurTexCoord = gl_FragCoord.xy / ssaoBlurTexSize;
float ssao = texture(ssaoBlurTexture, ssaoBlurTexCoord).r;
vec4 ambient = p3d_Material.ambient * p3d_LightModel.ambient * diffuseTex * ssao;
// ...
// ...
vec4 result = vec4(0);
for (int i = 0; i < size2; ++i) {
x = size - xCount;
y = yCount - size;
result +=
texture
( blurTexture
, texCoord
+ vec2(x * parameters.x, y * parameters.x)
);
xCount -= 1;
if (xCount < countMin) { xCount = countMax; yCount -= 1; }
}
result = result / size2;
// ...
parameters.x
— это параметр разделения. // ...
float focalLengthSharpness = 100;
float blurRate = 6;
// ...
focalLengthSharpness
влияет на то, насколько расфокусированной будет сцена на фокусном расстоянии. Чем меньше focalLengthSharpness
, тем более расфокусированной будет сцена на фокусном расстоянии. blurRate
влияет на скорость размытия сцены при отдалении от фокусного расстояния. Чем меньше blurRate
, тем менее размытой будет сцена при отдалении от точки фокусировки. // ...
vec4 focusColor = texture(focusTexture, texCoord);
vec4 outOfFocusColor = texture(outOfFocusTexture, texCoord);
// ...
// ...
vec4 position = texture(positionTexture, texCoord);
// ...
// ...
float blur =
clamp
( pow
( blurRate
, abs(position.y - focalLength.x)
)
/ focalLengthSharpness
, 0
, 1
);
// ...
fragColor = mix(focusColor, outOfFocusColor, blur);
// ...
blur
к единице, тем больше оно будет использовать outOfFocusColor
. Нулевое значение blur
означает, что этот фрагмент целиком находится в фокусе. При blur >= 1
этот фрагмент полностью расфокусирован. // ...
float levels = 8;
// ...
// ...
vec4 texColor = texture(posterizeTexture, texCoord);
// ...
// ...
vec3 grey = vec3((texColor.r + texColor.g + texColor.b) / 3.0);
vec3 grey1 = grey;
grey = floor(grey * levels) / levels;
texColor.rgb += (grey - grey1);
// ...
// ...
fragColor = texColor;
// ...
levels
. // ...
int pixelSize = 5;
// ...
// ...
float x = int(gl_FragCoord.x) % pixelSize;
float y = int(gl_FragCoord.y) % pixelSize;
x = floor(pixelSize / 2.0) - x;
y = floor(pixelSize / 2.0) - y;
x = gl_FragCoord.x + x;
y = gl_FragCoord.y + y;
// ...
// ...
fragColor = texture(pixelizeTexture, vec2(x, y) / texSize);
// ...
// ...
float amount = 0.8;
// ...
// ...
float neighbor = amount * -1;
float center = amount * 4 + 1;
// ...
amount * -1
. Текущий фрагмент умножается на amount * 4 + 1
. // ...
vec3 color =
texture(sharpenTexture, vec2(gl_FragCoord.x + 0, gl_FragCoord.y + 1) / texSize).rgb
* neighbor
+ texture(sharpenTexture, vec2(gl_FragCoord.x - 1, gl_FragCoord.y + 0) / texSize).rgb
* neighbor
+ texture(sharpenTexture, vec2(gl_FragCoord.x + 0, gl_FragCoord.y + 0) / texSize).rgb
* center
+ texture(sharpenTexture, vec2(gl_FragCoord.x + 1, gl_FragCoord.y + 0) / texSize).rgb
* neighbor
+ texture(sharpenTexture, vec2(gl_FragCoord.x + 0, gl_FragCoord.y - 1) / texSize).rgb
* neighbor
;
// ...
// ...
fragColor = vec4(color, texture(sharpenTexture, texCoord).a);
// ...
// ...
float amount = 0.1;
// ...
amount
управляет заметностью зернистости плёнки. Чем значение выше, тем больше «снега» на картинке.// ...
uniform float osg_FrameTime;
//...
float toRadians = 3.14 / 180;
//...
float randomIntensity =
fract
( 10000
* sin
(
( gl_FragCoord.x
+ gl_FragCoord.y
* osg_FrameTime
)
* toRadians
)
);
// ...
Time Since F1 = 00 01 02 03 04 05 06 07 08 09 10
Frame Number = F1 F3 F4 F5 F6
osg_FrameTime = 00 02 04 07 08
osg_FrameTime
предоставляется Panda3D. Время кадра — это временная метка с информацией о том, сколько секунд прошло после первого кадра. Пример кода использует его для анимирования зернистости плёнки, котому что osg_FrameTime
в каждом кадре будет разным. // ...
( gl_FragCoord.x
+ gl_FragCoord.y
* 8009 // Large number here.
// ...
osg_FrameTime
большим числом. Чтобы не видеть паттернов, можно попробовать разные числа. // ...
* sin
(
( gl_FragCoord.x
+ gl_FragCoord.y
* someNumber
// ...
sin
на osg_FrameTime
.input = (gl_FragCoord.x + gl_FragCoord.y * osg_FrameTime) * toRadians
frame(10000 * sin(input)) =
fract(10000 * sin(6.977777777777778)) =
fract(10000 * 0.6400723818964882) =
sin
используется как функция хеширования. Координаты фрагмента хешируются выходными значениями sin
. Благодаря этого появляется удобное свойство — какими бы ни были входные данные (большими или малыми), выходной интервал будет находиться в интервале от минус единицы до единицы.fract(10000 * sin(6.977777777777778)) =
fract(10000 * 0.6400723818964882) =
fract(6400.723818964882) =
0.723818964882
sin
в сочетании с fract
также используется как генератор псевдослучайных чисел.>>> [floor(fract(4 * sin(x * toRadians)) * 10) for x in range(0, 10)]
[0, 0, 1, 2, 2, 3, 4, 4, 5, 6]
>>> [floor(fract(10000 * sin(x * toRadians)) * 10) for x in range(0, 10)]
[0, 4, 8, 0, 2, 1, 7, 0, 0, 5]
fract(10000 * sin(...))
детерминированы, паттерн узнаётся гораздо слабее.sin
равен сначала 1, потом 10, потом 100, а затем 1000.sin
паттерн становится всё меньше заметен. По этой причине в коде sin
умножается на 10 000. // ...
vec2 texSize = textureSize(filmGrainTexture, 0).xy;
vec2 texCoord = gl_FragCoord.xy / texSize;
vec4 color = texture(filmGrainTexture, texCoord);
// ...
// ...
amount *= randomIntensity;
color.rgb += amount;
// ...
// ...
fragColor = color;
// ...
К сожалению, не доступен сервер mySQL