3D Game Shaders For Beginners
Adding reflections can really ground your scene. Wet and shiny objects spring to life as nothing makes something look wet or shiny quite like reflections. With reflections, you can really sell the illusion of water and metallic objects.
In the lighting section, you simulated the reflected, mirror-like image of the light source. This was the process of rendering the specular reflection. Recall that the specular light was computed using the reflected light direction. Similarly, using screen space reflection or SSR, you can simulate the reflection of other objects in the scene instead of just the light source. Instead of the light ray coming from the source and bouncing off into the camera, the light ray comes from some object in the scene and bounces off into the camera.
SSR works by reflecting the screen image onto itself using only itself. Compare this to cube mapping which uses six screens or textures. In cube mapping, you reflect a ray from some point in your scene to some point on the inside of a cube surrounding your scene. In SSR, you reflect a ray from some point on your screen to some other point on your screen. By reflecting your screen onto itself, you can create the illusion of reflection. This illusion holds for the most part but SSR does fail in some cases as you’ll see.
Ray Marching
Screen space reflection uses a technique known as ray marching to determine the reflection for each fragment. Ray marching is the process of iteratively extending or contracting the length or magnitude of some vector in order to probe or sample some space for information. The ray in screen space reflection is the position vector reflected about the normal.
Intuitively, a light ray hits some point in the scene, bounces off, travels in the opposite direction of the reflected position vector, bounces off the current fragment, travels in the opposite direction of the position vector, and hits the camera lens allowing you to see the color of some point in the scene reflected in the current fragment. SSR is the process of tracing the light ray’s path in reverse. It tries to find the reflected point the light ray bounced off of and hit the current fragment. With each iteration, the algorithm samples the scene’s positions or depths, along the reflection ray, asking each time if the ray intersected with the scene’s geometry. If there is an intersection, that position in the scene is a potential candidate for being reflected by the current fragment.
Ideally there would be some analytical method for determining the first intersection point exactly. This first intersection point is the only valid point to reflect in the current fragment. Instead, this method is more like a game of battleship. You can’t see the intersections (if there are any) so you start at the base of the reflection ray and call out coordinates as you travel in the direction of the reflection. With each call, you get back an answer of whether or not you hit something. If you do hit something, you try points around that area hoping to find the exact point of intersection.
Here you see ray marching being used to calculate each fragment’s reflected point. The vertex normal is the bright green arrow, the position vector is the bright blue arrow, and the bright red vector is the reflection ray marching through view space.
Vertex Positions
Like SSAO, you’ll need the vertex positions in view space. Referrer back to SSAO for details.
Vertex Normals
To compute the reflections, you’ll need the vertex normals in view space. Referrer back to SSAO for details.
Here you see SSR using the normal mapped normals instead of the vertex normals. Notice how the reflection follows the ripples in the water versus the more mirror like reflection shown earlier.
To use the normal maps instead, you’ll need to transform the normal mapped normals from tangent space to view space just like you did in the lighting calculations. You can see this being done in normal.frag.
Position Transformations
Just like SSAO, SSR goes back and forth between the screen and view space. You’ll need the camera lens’ projection matrix to transform points in view space to clip space. From clip space, you’ll have to transform the points again to UV space. Once in UV space, you can sample a vertex/fragment position from the scene which will be the closest position in the scene to your sample. This is the screen space part in screen space reflection since the «screen» is a texture UV mapped over a screen shaped rectangle.
Reflected UV Coordinates
There are a few ways you can implement SSR. The example code starts the reflection process by computing a reflected UV coordinate for each screen fragment. You could skip this part and go straight to computing the reflected color instead, using the final rendering of the scene.
Recall that UV coordinates range from zero to one for both U and V. The screen is just a 2D texture UV mapped over a screen-sized rectangle. Knowing this, the example code doesn’t actually need the final rendering of the scene to compute the reflections. It can instead calculate what UV coordinate each screen pixel will eventually use. These calculated UV coordinates can be saved to a framebuffer texture and used later when the scene has been rendered.
Here you see the reflected UV coordinates. Without even rendering the scene yet, you can get a good feel for what the reflections will look like.
You’ll need the camera lens’ projection matrix as well as the interpolated vertex positions and normals in view space.
Like the other effects, SSR has a few parameters you can adjust. Depending on the complexity of the scene, it may take you awhile to find the right settings. Getting screen space reflections to look just right tends to be difficult when reflecting complex geometry.
The maxDistance parameter controls how far a fragment can reflect. In other words, it controls the maximum length or magnitude of the reflection ray.
The resolution parameter controls how many fragments are skipped while traveling or marching the reflection ray during the first pass. This first pass is to find a point along the ray’s direction where the ray enters or goes behind some geometry in the scene. Think of this first pass as the rough pass. Note that the resolution ranges from zero to one. Zero will result in no reflections while one will travel fragment-by-fragment along the ray’s direction. A resolution of one can slow down your FPS considerably especially with a large maxDistance .
The steps parameter controls how many iterations occur during the second pass. This second pass is to find the exact point along the reflection ray’s direction where the ray immediately hits or intersects with some geometry in the scene. Think of this second pass as the refinement pass.
The thickness controls the cutoff between what counts as a possible reflection hit and what does not. Ideally, you’d like to have the ray immediately stop at some camera-captured position or depth in the scene. This would be the exact point where the light ray bounced off, hit your current fragment, and then bounced off into the camera. Unfortunately the calculations are not always that precise so thickness provides some wiggle room or tolerance. You’ll want the thickness to be as small as possible—just a short distance beyond a sampled position or depth.

You’ll find that as the thickness gets larger, the reflections tend to smear in places.

Going in the other direction, as the thickness gets smaller, the reflections become noisy with tiny little holes and narrow gaps.
Gather the current fragment’s position, normal, and reflection about the normal. positionFrom is a vector from the camera position to the current fragment position. normal is a vector pointing in the direction of the interpolated vertex normal for the current fragment. pivot is the reflection ray or vector pointing in the reflected direction of the positionFrom vector. It currently has a length or magnitude of one.
Calculate the start and end point of the reflection ray in view space.
Project or transform these start and end points from view space to screen space. These points are now fragment positions which correspond to pixel positions on the screen. Now that you know where the ray starts and ends on the screen, you can travel or march along its direction in screen space. Think of the ray as a line drawn on the screen. You’ll travel along this line using it to sample the fragment positions stored in the position framebuffer texture.

Note that you could march the ray through view space but this may under or over sample scene positions found in the position framebuffer texture. Recall that the position framebuffer texture is the size and shape of the screen. Every screen fragment or pixel corresponds to some position captured by the camera. A reflection ray may travel a long distance in view space, but in screen space, it may only travel through a few pixels. You can only sample the screen’s pixels for positions so it is inefficient to potentially sample the same pixels over and over again while marching in view space. By marching in screen space, you’ll more efficiently sample the fragments or pixels the ray actually occupies or covers.
The first pass will begin at the starting fragment position of the reflection ray. Convert the fragment position to a UV coordinate by dividing the fragment’s coordinates by the position texture’s dimensions.
Calculate the delta or difference between the X and Y coordinates of the end and start fragments. This will be how many pixels the ray line occupies in the X and Y dimension of the screen.

To handle all of the various different ways (vertical, horizontal, diagonal, etc.) the line can be oriented, you’ll need to keep track of and use the larger difference. The larger difference will help you determine how much to travel in the X and Y direction each iteration, how many iterations are needed to travel the entire line, and what percentage of the line does the current position represent.
useX is either one or zero. It is used to pick the X or Y dimension depending on which delta is bigger. delta is the larger delta of the two X and Y deltas. It is used to determine how much to march in either dimension each iteration and how many iterations to take during the first pass.
Calculate how much to increment the X and Y position by using the larger of the two deltas. If the two deltas are the same, each will increment by one each iteration. If one delta is larger than the other, the larger delta will increment by one while the smaller one will increment by less than one. This assumes the resolution is one. If the resolution is less than one, the algorithm will skip over fragments.
For example, say the resolution is 0.5. The larger dimension will increment by two fragments instead of one.
To move from the start fragment to the end fragment, the algorithm uses linear interpolation.
search1 ranges from zero to one. When search1 is zero, the current position is the start fragment. When search1 is one, the current position is the end fragment. For any other value, the current position is somewhere between the start and end fragment.
search0 is used to remember the last position on the line where the ray missed or didn’t intersect with any geometry. The algorithm will later use search0 in the second pass to help refine the point at which the ray touches the scene’s geometry.
hit0 indicates there was an intersection during the first pass. hit1 indicates there was an intersection during the second pass.
The viewDistance value is how far away from the camera the current point on the ray is. Recall that for Panda3D, the Y dimension goes in and out of the screen in view space. For other systems, the Z dimension goes in and out of the screen in view space. In any case, viewDistance is how far away from the camera the ray currently is. Note that if you use the depth buffer, instead of the vertex positions in view space, the viewDistance would be the Z depth.
Make sure not to confuse the viewDistance value with the Y dimension of the line being traveled across the screen. The viewDistance goes from the camera into scene while the Y dimension of the line travels up or down the screen.
The depth is the view distance difference between the current ray point and scene position. It tells you how far behind or in front of the scene the ray currently is. Remember that the scene positions are the interpolated vertex positions stored in the position framebuffer texture.
You can now begin the first pass. The first pass runs while i is less than the delta value. When i reaches delta , the algorithm has traveled the entire length of the line. Remember that delta is the larger of the two X and Y deltas.
Advance the current fragment position closer to the end fragment. Use this new fragment position to look up a scene position stored in the position framebuffer texture.
Calculate the percentage or portion of the line the current fragment represents. If useX is zero, use the Y dimension of the line. If useX is one, use the X dimension of the line.
When frag equals startFrag , search1 equals zero since frag — startFrag is zero. When frag equals endFrag , search1 is one since frag — startFrag equals delta .
search1 is the percentage or portion of the line the current position represents. You’ll need this to interpolate between the ray’s view-space start and end distances from the camera.
Using search1 , interpolate the view distance (distance from the camera in view space) for the current position you’re at on the reflection ray.
You may be tempted to just interpolate between the view distances of the start and end view-space positions but this will give you the wrong view distance for the current position on the reflection ray. Instead, you’ll need to perform perspective-correct interpolation which you see here.
Calculate the difference between the ray’s view distance at this point and the sampled view distance of the scene at this point.
If the difference is between zero and the thickness, this is a hit. Set hit0 to one and exit the first pass. If the difference is not between zero and the thickness, this is a miss. Set search0 to equal search1 to remember this position as the last known miss. Continue marching the ray towards the end fragment.
At this point you have finished the first pass. Set the search1 position to be halfway between the position of the last miss and the position of the last hit.
You can now begin the second pass. If the reflection ray didn’t hit anything in the first pass, skip the second pass.
As you did in the first pass, use the current position on the ray line to sample a position from the scene.
Interpolate the view distance for the current ray line position and calculate the camera distance difference between the ray at this point and the scene.
If the depth is within bounds, this is a hit. Set hit1 to one and set search1 to be halfway between the last known miss position and this current hit position. If the depth is not within bounds, this is a miss. Set search1 to be halfway between this current miss position and the last known hit position. Move search0 to this current miss position. Continue this back and forth search while i is less than steps .
You’re now done with the second and final pass but before you can output the reflected UV coordinates, you’ll need to calculate the visibility of the reflection. The visibility ranges from zero to one. If there wasn’t a hit in the second pass, the visibility is zero.
If the reflected scene position’s alpha or w component is zero, the visibility is zero. Note that if w is zero, there was no scene position at that point.
One of the ways in which screen space reflection can fail is when the reflection ray points in the general direction of the camera. If the reflection ray points towards the camera and hits something, it’s most likely hitting the back side of something facing away from the camera.
To handle this failure case, you’ll need to gradually fade out the reflection based on how much the reflection vector points to the camera’s position. If the reflection vector points directly in the opposite direction of the position vector, the visibility is zero. Any other direction results in the visibility being greater than zero.
Remember to normalize both vectors when taking the dot product. unitPositionFrom is the normalized position vector. It has a length or magnitude of one.
As you sample scene positions along the reflection ray, you’re hoping to find the exact point where the reflection ray first intersects with the scene’s geometry. Unfortunately, you may not find this particular point. Fade out the reflection the further it is from the intersection point you did find.
Fade out the reflection based on how far way the reflected point is from the initial starting point. This will fade out the reflection instead of it ending abruptly as it reaches maxDistance .
If the reflected UV coordinates are out of bounds, set the visibility to zero. This occurs when the reflection ray travels outside the camera’s frustum.
Set the blue and alpha component to the visibility as the UV coordinates only need the RG or XY components of the final vector.
The final fragment color is the reflected UV coordinates and the visibility.
Specular Map
In addition to the reflected UV coordinates, you’ll also need a specular map. The example code creates one using the fragment’s material specular properties.
The specular fragment shader is quite simple. Using the fragment’s material, the shader outputs the specular color and uses the alpha channel for the shininess. The shininess is mapped to a range of zero to one. In Blender, the maximum specular hardness or shininess is 511. When exporting from Blender to Panda3D, 511 is exported as 127.75. Feel free to adjust the shininess to range of zero to one however you see fit for your particular stack.
The example code generates a specular map from the material specular properties but you could create one in GIMP, for example, and attach that as a texture to your 3D model. For instance, say your 3D treasure chest has shiny brackets on it but nothing else should reflect the environment. You can paint the brackets some shade of gray and the rest of the treasure chest black. This will mask off the brackets, allowing your shader to render the reflections on only the brackets and nothing else.
Scene Colors

You’ll need to render the parts of the scene you wish to reflect and store this in a framebuffer texture. This is typically just the scene without any reflections.
Reflected Scene Colors
Here you see the reflected colors saved to a framebuffer texture.
Once you have the reflected UV coordinates, looking up the reflected colors is fairly easy. You’ll need the reflected UV coordinates texture and the color texture containing the colors you wish to reflect.
Using the UV coordinates for the current fragment, look up the reflected color.
Recall that the reflected UV texture stored the visibility in the B or blue component. This is the alpha channel for the reflected colors framebuffer texture.
The fragment color is a mix between no reflection and the reflected color based on the visibility. The visibility was computed during the reflected UV coordinates step.
Blurred Reflected Scene Colors

Now blur the reflected scene colors and store this in a framebuffer texture. The blurring is done using a box blur. Refer to the SSAO blurring step for details.
The blurred reflected colors are used for surfaces that have a less than mirror like finish. These surfaces have tiny little hills and valleys that tend to diffuse or blur the reflection. I’ll cover this more during the roughness calculation.
Reflections
To generate the final reflections, you’ll need the three framebuffer textures computed earlier. You’ll need the reflected colors, the blurred reflected colors, and the specular map.
Look up the specular amount and shininess, the reflected scene color, and the blurred reflected scene color.
Map the specular color to a greyscale value. If the specular amount is none, set the frag color to nothing and return.
Later on, you’ll multiply the final reflection color by the specular amount. Multiplying by the specular amount allows you to control how much a material reflects its environment simply by brightening or darkening the greyscale value in the specular map.
Using the dot product to produce the greyscale value is just a short way of summing the three color components.
Calculate the roughness based on the shininess value set during the specular map step. Recall that the shininess value was saved in the alpha channel of the specular map. The shininess determined how spread out or blurred the specular reflection was. Similarly, the roughness determines how blurred the reflection is. A roughness of one will produce the blurred reflection color. A roughness of zero will produce the non-blurred reflection color. Doing it this way allows you to control how blurred the reflection is just by changing the material’s shininess value.
The example code generates a roughness map from the material specular properties but you could create one in GIMP, for example, and attach that as a texture to your 3D model. For instance, say you have a tiled floor that has polished tiles and scratched up tiles. The polished tiles could be painted a more translucent white while the scratched up tiles could be painted a more opaque white. The more translucent/transparent the greyscale value, the more the shader will use the blurred reflected color. The scratched tiles will have a blurry reflection while the polished tiles will have a mirror like reflection.
Mix the reflected color and blurred reflected color based on the roughness. Multiply that vector by the specular amount and then set that value as the fragment color.
The reflection color is a mix between the reflected scene color and the blurred reflected scene color based on the roughness. A high roughness will produce a blurry reflection meaning the surface is rough. A low roughness will produce a clear reflection meaning the surface is smooth.
Что такое SSR в Таркове?
Вот что необходимо сделать:Запустите Escape from Tarkov.Откройте меню Настройки и кликните по вкладке Графика.Установите Разрешение экрана равным собственному разрешению вашего монитора.Выберите Режим экрана Fullscreen.Убедитесь, что галочка Верт. . Установите среднее Качество текстур.
Как поднять производительность в Таркове?
Установите программу WinOptimizer (скачать по прямой ссылке) и включите в ней игровой режим, который завершит бесполезные фоновые процессы во время запуска игр и повысит производительность в игре.
Сколько нужно оперативной памяти для Таркова?
Системные требования Escape from TarkovМинимальныеПроцессорIntel Core i3 или AMD Ryzen 3ВидеокартаNVIDIA GeForce GTX 1650Оперативная память8 GB DDR4 с частотой от 3200 МгцМесто на диске8 GB на SSD
Что такое Player RTT?
RTT (англ. Round Trip Time) — интервал времени между отправкой пакета и окончанием его обработки на принимающей стороне. RTT позволяет определять двусторонние задержки по маршруту, т.
Сколько ядер использует тарков?
Процессор: 4-ядерный с тактовой частотой от 3,2 ГГц (Intel i5, i7), от 3,6 ГГц (AMD FX, Athlon). Видеокарта: любая на 2 Гбайт и с поддержкой DirectX 11. Оперативная память: 8 гигабайт.
Почему тормозит тарков?
Escape from Tarkov тормозит, когда снижается частота кадров, с которой картинка выводится на монитор, и лагает, когда задержка при обращении к серверу или любому другому хосту слишком высокая. Именно поэтому «лаги» могут быть только в сетевых играх.
Как поставить фпс в тарков?
Перейдите в Настройки Нажмите на In-Game. Перейдите на внутриигровой счетчик FPS. В раскрывающемся списке внутриигрового счетчика FPS выберите позицию, в которой вы хотите, чтобы отображался FPS, т.
Какие видеокарты тянут тарков?
Только топовые видеокарты 3080-3090 и 6800XT-6900XT позволят насладиться побегом из Таркова.
Как включить MIP Streaming тарков?
Для этого перейдите по пути «%AppData%\Battlestate Games\Escape from Tarkov\Settings» (нажмите Win+R, введите путь без кавычек, нажмите ОК), откройте файл Graphics. ini любым текстовым редактором, найдите в нём параметр «MipStreaming» и замените значение «true» на «false».
Что такое SSR в Таркове? Ответы пользователей
HBAO или Horizon-Based Ambient Occlusion. Делает игру более реалистичной, но съедает ресурсы, поэтому выключите ее. SSR — выкл. Отражения.
Отключите SSR. Для параметра Анизотропная фильтрация выберите Только текстуры. Установите Резкость в 1.3. Задайте Лимит кадров в лобби .
Выключите SSR . Установите Анизотропную фильтрацию для каждой текстуры. От FPS на внутриигровой шум , существует множество других настроек, к которым геймеры .
Тарков гайд — Настройка и оптимизация игры Escape from Tarkov 2022. LeaDoXo · ▻ 4:45. №20 КОРОТКО как настроить графику в Таркове · Medved Petrovi4 Play.
Об этом уже писалось в статье про долгие загрузки игры в Таркове, . SSAO/SSR: мягкое затенение и освещение, отключается как излишество во благо ФПС.
9900К+1080Ти = выкрутил в 2К разрешении все на ультры и максимумы, HBAO и SSR также, тлько ресемплинг оставил 1х. все полузнки на максимум, .
SSLR: Screen Space Local Reflections в AAA-играх

Привет, друг! В этот раз я опять подниму вопрос о графике в ААА-играх. Я уже разобрал методику HDRR (не путать с HDRI) тут и чуть-чуть поговорил о коррекции цвета. Сегодня я расскажу, что такое SSLR (так же известная как SSPR, SSR): Screen Space Local Reflections. Кому интересно — под кат.
Введение в Deferred Rendering
Для начала введу такое понятие как Deferred Rendering (не путать с Deferred Shading, т.к. последнее относится к освещению). В чем суть Deferred Rendering? Дело в том, что все эффекты (такие как освещение, глобальное затенение, отражения, DOF) можно отделить от геометрии и реализовать эти эффекты как особый вид постпроцессинга. К примеру, что нужно, чтобы применить DOF (Depth Of Field, размытие на дальних расстояниях) к нашей сцене? Иметь саму сцену (Color Map) и иметь информацию о позиции текселя (другими словами на сколько пиксель далеко от камеры). Далее — все просто. Применяем Blur к Color Map, где радиус размытия будет зависеть от глубины пикселя (из Depth Map). И если взглянуть на результат — чем дальше объект, тем сильнее он будет размыт. Так что же делает методика Deferred Rendering? Она строит так называемый GBuffer, который, обычно, в себя включает три текстуры (RenderTarget):
- Color map (информация о диффузной составляющий или просто цвет пикселя)

- Normal map (информация о нормали “пикселя”)

- Depth map (информация о позиции “пикселя”, тут храним только глубину)

В случае с Color map, Normal map вроде все понятно, это обычные Surface.Color текстуры: пожалуй, за исключением того, что вектор нормали может лежать в пределах [-1, 1] (используется простая упаковка вектора в формат [0, 1]).
А вот ситуация с Depth map становится непонятной. Как же Depth map хранит в себе информацию о позиции пикселя, да еще и одним числом? Если говорить сильно упрощенно, трансформация примитива:
Дает нам экранные координаты:
И некоторую информацию о том, насколько “далеко” от камеры пиксель:
Исходя из этого UV нам не нужен, т.к. при рисовании обычного квада на весь экран он и так известен. Поэтому стоит хранить в карте глубины не позицию пикселя, а только глубину.
В дальнейшем мы сможем реконструировать позицию пикселя очень простым способом:
Напомню, что для построения GBuffer необходима такая методика как MRT (Multiple Render Targets), которая рисует модель сразу в несколько Render Target (причем в каждом RT содержится разная информация). Одно из правил MRT — размерность всех Render Target должна быть одинаковой. В случае Color Map, Normal Map — Surface.Color: 32-ух битная RT, где на каждый канал ARGB приходится по 8 бит, т.е. 256 градаций от 0 до 1.
Благодаря такому подходу мы можем применять сложные эффекты к любой геометрии, например самый популярный Screen Space эффект: SSAO (Screen Space Ambient Occlusion). Этот алгоритм анализирует буферы глубины и нормали, считая уровень затенения. Весь алгоритм я описывать не буду, он уже описывался на хабре, скажу лишь то, что задача алгоритма сводится к трассировки карты глубины: у нас есть набор случайных векторов, направленных из считаемого “пикселя” и нам нужно найти кол-во пересечений с геометрией.
Пример эффекта (слева без SSAO, справа с SSAO):

Так же Deferred Shading является Screen Space эффектом. Т.е. для каждого источника света на экране (без всяких оптимизаций) мы рисуем квад в режиме Additive в так называемый RenderTarget: Light Map. И зная мировую позицию “пикселя”, его нормаль, позицию источника света — мы можем посчитать освещенность этого пикселя.
Пример Deferred Shading (освещение выполнено отложено, после отрисовки геометрии):

Достоинства и проблемы Screen Space эффектов
Самый главный плюс Screen Space эффектов — независимость сложности эффекта от геометрии.
Самый главный минус — локальность всех эффектов. Дело в том, что мы постоянно будем сталкиваться с Information Lost, во многих случаях это сильно зависит обзора, поскольку SSE зависит от смежных глубин текселей, которые могут быть сгенерированы любой геометрией.
Ну и стоит отменить, что Screen Space эффекты выполняются полностью на GPU и являются пост-процессингом.
Наконец SSLR
После всей теории мы подошли к такому эффекту, как Screen Space Local Reflections: локальные отражения в экранном пространстве.
Для начала разберемся с перспективной проекцией:

Горизонтальный и вертикальный угол зрения задается FOV (обычно 45 градусов, я предпочитаю 60 градусов), в виртуальной камере они разные т.к. учитывается еще и Aspect Ratio (соотношение сторон).
Окно проекции (там, где мы оперируем UV-space данными) — это, что мы видим, на то мы проецируем нашу сцену.
Передняя и задняя плоскости отсечения это соответственно Near Plane, Far Plane, задаются так же в проекцию как параметры. Делать в случае Deferred Rendering слишком большим значением Far Plane стоит, т.к. точность Depth Buffer сильно упадет: все зависит от сцены.
Теперь, зная матрицу проекции и позицию на окне проекции (а так же глубину) для каждого пикселя мы вычисляем его позицию следующим образом:
После нам нужно найти вектор взгляда на этот пиксель:
В качестве CameraPosition выступает позиция камеры.
И найти отражение этого вектора от нормали в текущем пикселе:
Далее задача сводится к трассировке карты глубины. Т.е. нам нужно найти пересечение отраженного вектора с какой-либо геометрией. Понятное дело, что любая трассировка производится через итерации. И мы в них сильно ограниченны. Т.к. каждая выборка из Depth Map стоит времени. В моем варианте мы берем некоторое начальное приближение L и динамически меняем его исходя из расстояния между нашим текселем и позицией, которую мы “восстановили”:
Вспомогательные функции, перевод мировой точки на экранное пространство:
После завершения итераций мы имеет позицию “пересечения с отраженной геометрией”. А наше значение nuv будет проекцией этого пересечения на экран, т.е. nuv.xy – это UV координаты в экранном нашем пространстве, а nuv.z это восстановленная глубина (т.е. abs(GetDepth(nuv.xy)-nuv.z) должен быть очень маленьким).
В конце итераций L будет показывать расстояние отраженного пикселя. Последний этап — собственно добавление отражения к Color Map:
Разбавим теорию иллюстрациями, исходное изображение (содержание Color Map из GBuffer):

После компиляции шейдера (отражения) мы получим следующую картину (Color Map из GBuffer + результат шейдера SSLR):

Не густо. И тут стоит еще раз напомнить, что Space-Screen эффекты это сплошной Information Lost (примеры выделены в красные рамки).
Дело в том, что если вектор отражения выходит за пределы Space-Screen – информация о Color-карте становится недоступной и мы видим Clamping нашего UV.
Чтобы частично исправить эту проблему, можно ввести дополнительный коэффициент, который будет отражать “дальность” отражения. И далее по этому коэффициенту мы будем затенять отражение, проблема частично решается:
Результат, отражение умноженное на error (попытка убрать артефакт SSLR — information lost):

Уже лучше, но мы замечаем еще одну проблему, что будет, если вектор отразится в направлении камеры? Clamping’а UV происходить не будет, однако, несмотря на актуальность UV (x > 0, y > 0, x < 1, y < 1) он будет неверным:

Эту проблему так же можно частично решить, если как-нибудь ограничить углы допустимых отражений. Для этого идеально подходит фишка с углами от эффекта Френеля:
Чуть-чуть модифицируем формулу:
Значения Френеля, с учетом Normal-маппинга (значения fresnel-переменной для SSLR-алгоритма):

Те области, которые отражаются в “камеру” будут черными, и их мы не учитываем (взамен можно сделать fade в кубическую текстуру).
Отражение, умноженное на error и fresnel (попытка удалить большую часть артефактов SSLR):

Кстати, значение Fresnel стоит лимитировать по какому-либо параметру, т.к. из-за “шероховатости” нормалей значение будет на порядок больше единицы (или другого числа-лимитера).
И завершающий этап сегодняшний статьи — это размытие отражений, т.к. идеальное отражение только у зеркала. Степень размытия можно считать как 1-error (чем дальше отраженный пиксель — тем сильнее размыт). Это будет своеобразный вес размытия и хранить его можно в альфа-канале RT-отражений.
Результат (финальное изображение с убранными артефактами и с размытыми отражениями):

Заключение
Так же, стоит добавить некоторую информацию об отражающей способности: насколько четкое отражение, насколько поверхность вообще способна отражать, в те места где SSLR не работает — добавить статическое отражение кубической текстуры.
Конечно, Space-Screen эффекты не являются честными, и разработчики стараются скрыть артефакты, но сейчас в реалтайме подобное (при сложной геометрии) сделать невозможно. А без подобных эффектов игра начинает выглядеть как-то не так. Я описал общую методику SSLR: основные моменты из шейдера я привел. Код, к сожалению, прикрепить не могу, т.к. в проекте слишком много зависимостей.
Товарищи 3D эксперты, подскажите пжл что это такое?
Встречается на отражениях и тенях в Atomic Heart и других играх, и очень бросается в глаза.
С тенями, по большей части у динамических объектов, и AO происходит тоже самое:
Понятно, что это SSR. Почему он шероховатым/шахматным становится на краях? То же самое и с тенями происходит.
Потому что экран заканчивается и отражать становится нечего, это ж вам не RTX. А шероховатость получается из-за небольшого разброса лучей, повторяющаяся мелкая текстура используется для создания "шума".
В пределы экрана на скрине объект полностью помещается. Что мешает его полностью отразить в SSR? В Atomic Heart тоже самое происходит и с тенями. Такое встречается в малом количестве игр с SSR и тенями.
А шероховатость получается из-за небольшого разброса лучей
Каких лучей? Сам написал, что это не RT.
Если ты специалист в этом, скинь пжл, где можно подробнее прочитать, как получается такой эффект?
С тенями тоже шумовые паттерны используются для убирания пикселизации, вот и вылезает местами. Хотя там разные бывают подходы конечно, фиг знает какая именно тут реализация — возможно, просчитанные тени накидываются в отдельный буфер, который потом "зашумляется" этим паттерном и накладывается как бы постэффектом, вот и вылезают подобные артефакты. Опять же, не стоит исключать и какую-то программную накладку, в результате которой ты увидел то, что не должен был увидеть, результат вычислений не был сглажен.
Каких лучей? Сам написал, что это не RT.
SSR это по сути рейтрейсинг в пределах уже отрендеренной картинки, реализованный через шейдер и использующий карты нормалей и глубину каждого пикселя в качестве условной "геометрии". Самые настоящие лучи, просто их пересечения вычисляются не с полигонами в сцене, а с пикселями (условными вокселями) на уже отрисованной "заготовке" кадра игры. Подобными же "шейдерными лучами" реализованы алгоритмы мягкого затенения типа SSAO, а также новомодные попиксельные тени, позволяющие отбрасывать четкую индивидуальную тень буквально от каждой мелкой травинки или камешка\соринки, ну и даже реалтаймовый эффект рассеянного отражения света от различных поверхностей — ну это когда какой-нибудь ярко освещенный предмет даёт пятно отраженного света на рядом находящуюся стену, например, подкрашивая её своим цветом.
Занятно, что все эти чудеса с "шейдерным рейтрейсингом" по сути зародились еще в середине нулевых, когда в играх то там, то сям начало использоваться рельефное текстурирование — это когда смотришь на поверхность под углом, а она аж выпирает, кирпичи как будто геометрией сделаны или земля бугристая, а по факту всего лишь текстура с картой высот. Но благодаря таким вот лучам происходит рейтрейсинг внутри неё и определяются пересечения с условными вокселями, позволяя отрисовать убедительно объемный рельеф. Где детальнее почитать так сходу не скажу, просто гугли все эти термины, которые сам же используешь.