#c #opengl #glsl #2d #sfml
Вопрос:
В настоящее время я пытаюсь нарисовать 2D-сетку на одном квадрате, используя только шейдеры. Я использую SFML в качестве графической библиотеки и sf::View для управления камерой. До сих пор мне удавалось нарисовать сглаженную многоуровневую сетку. Первый уровень (синий) очерчивает фрагмент, а второй уровень (серый) очерчивает плитки внутри фрагмента.
Теперь я хотел бы изменить уровни сетки в зависимости от расстояния до камеры. Например, сетка фрагментов должна исчезать по мере увеличения масштаба камеры. То же самое следует сделать для сетки плиток после того, как сетка фрагментов полностью исчезнет.
Я не уверен, как это можно было бы реализовать, так как я все еще новичок в OpenGL и GLSL. Если у кого-нибудь есть какие-либо указания о том, как можно реализовать эту функциональность, пожалуйста, дайте мне знать.
Вершинный шейдер
#version 130 out vec2 texCoords; void main() { gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; texCoords = (gl_TextureMatrix[0] * gl_MultiTexCoord0).xy; }
Шейдер фрагментов
#version 130 uniform vec2 chunkSize = vec2(64.0, 64.0); uniform vec2 tileSize = vec2(16.0, 16.0); uniform vec3 chunkBorderColor = vec3(0.0, 0.0, 1.0); uniform vec3 tileBorderColor = vec3(0.5, 0.5, 0.5); uniform bool drawGrid = true; in vec2 texCoords; void main() { vec2 uv = texCoords.xy * chunkSize; vec3 color = vec3(1.0, 1.0, 1.0); if(drawGrid) { float aa = length(fwidth(uv)); vec2 halfChunkSize = chunkSize / 2.0; vec2 halfTileSize = tileSize / 2.0; vec2 a = abs(mod(uv - halfChunkSize, chunkSize) - halfChunkSize); vec2 b = abs(mod(uv - halfTileSize, tileSize) - halfTileSize); color = mix( color, tileBorderColor, smoothstep(aa, .0, min(b.x, b.y)) ); color = mix( color, chunkBorderColor, smoothstep(aa, .0, min(a.x, a.y)) ); } gl_FragColor.rgb = color; gl_FragColor.a = 1.0; }
Ответ №1:
Вам нужно разделить умножение в шейдере вершин на две части:
// have a variable to be interpolated per fragment out vec2 vertex_coordinate; ... { // this will store the coordinates of the vertex // before its projected (i.e. its "world" coordinates) vertex_coordinate = gl_ModelViewMatrix * gl_Vertex; // get your projected vertex position as before gl_Position = gl_ProjectionMatrix * vertex_coordinate; ... }
Затем в шейдере фрагментов вы меняете цвет в зависимости от координаты мировой вершины и положения камеры:
in vec2 vertex_coordinate; // have to update this value, every time your camera changes its position uniform vec2 camera_world_position = vec2(64.0, 64.0); ... { ... // calculate the distance from the fragment in world coordinates to the camera float fade_factor = length(camera_world_position - vertex_coordinate); // make it to be 1 near the camera and 0 if its more then 100 units. fade_factor = clamp(1.0 - fade_factor / 100.0, 0.0, 1.0); // update your final color with this factor gl_FragColor.rgb = color * fade_factor; ... }
Второй способ сделать это-использовать координаты проекции w
. Лично я предпочитаю вычислять расстояние в единицах пространства. Я не тестировал этот код, в нем могут быть некоторые тривиальные синтаксические ошибки, но если вы понимаете идею, вы можете применить ее любым другим способом.