Предположим, что x
, y
и z
определяют центр круга в мировом пространстве.Вы хотите нарисовать круг в плоскости, параллельной порту просмотра в проходе пространства экрана, где вы рисуете квад по всему окну просмотра.
Вам необходимо преобразовать центр круга из мирового пространства.координаты к нормализованным координатам устройства.Лучшим решением было бы сделать это на процессоре и установить единообразный результат.
Согласно коду вашего вопроса, это можно сделать и в вершинном шейдере.Но вам нужно сделать Перспективное деление после преобразования с помощью матрицы вида модели и матрицы проекции, чтобы преобразовать пространство клипа в форме точки для просмотра нормализованного пространства устройства:
uniform mat4 P;
uniform mat4 MV;
uniform float x;
uniform float y;
uniform float z;
varying vec3 cpt;
void main(){
vec4 cpt_h = projectionMatrix * modelViewMatrix * vec4(x, y, z, 1.0);
vec3 cpt = cpt_h.xyz / cpt_h.w;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
Если u_resolution
, это ширина и высота области просмотра, то координаты x и y фрагмента в нормализованном пространстве устройства могут быть вычислены следующим образом:
vec2 coord = gl_FragCoord.xy / u_resolution.xy * 2.0 - 1.0;
Но я рекомендую преобразовать центральную точкуот координат окружности до окна (пикселя), тогда радиус также можно задать в пикселях:
vec2 cpt_p = (cpt.xy * 0.5 + 0.5) * u_resolution.xy;
Для вычисления длины вектора вы можете использовать функцию GLSL length
.
Конечный фрагментный шейдер может выглядеть следующим образом:
varying vec3 cpt;
uniform vec2 u_resolution;
uniform float u_pixel_ratio; // device pixel ratio
uniform float r; // e.g. 100.0 means a radius of 100 pixel
float circle( vec2 _st, vec2 _center, float _radius )
{
// thickness of the circle in pixel
const float thickness = 20.0;
// distance to the center point in pixel
float dist = length(_st - _center);
return 1.0 - smoothstep(0.0, thickness/2.0, abs(_radius-dist));
}
void main(){
vec2 cpt_p = (cpt.xy * 0.5 + 0.5) * u_resolution.xy * u_pixel_ratio;
float point = circle(gl_FragCoord.xy, cpt_p, r);
gl_FragColor = vec4(point);
}
например, круг с радиусом 50,0 и толщиной 20,0:
![](https://i.stack.imgur.com/qIj5x.png)
Если вы хотите применить искажение перспективы к кругу, это означает, что размер круга уменьшается на расстояние, тогда вы должны установить радиус r
в мировых координатах.Вычислите точку на окружности и вычислите расстояние от точки до центральной точки окружности в вершинном шейдере в нормализованном пространстве устройства.Это радиус, который вы должны пройти от вершинного шейдера до фрагментного шейдера, дополнительного к центральной точке круга.
uniform mat4 P;
uniform mat4 MV;
uniform float x;
uniform float y;
uniform float z;
uniform float r; // e.g. radius in world space
varying vec3 cpt;
varying float radius;
void main(){
vec4 cpt_v = modelViewMatrix * vec4(x, y, z, 1.0);
vec4 rpt_v = vec4(cpt_v.x, cpt_v.y + r, cpt_v.zw);
vec4 cpt_h = projectionMatrix * cpt_v;
vec4 rpt_h = projectionMatrix * rpt_v;
cpt = cpt_h.xyz / cpt_h.w;
vec3 rpt = rpt_v.xyz / rpt_v.w;
radius = length(rpt-cpt);
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
varying vec3 cpt;
varying float radius;
uniform vec2 u_resolution;
uniform float u_pixel_ratio; // device pixel ratio
uniform float r; // e.g. 100.0 means a radius of 100 pixel
float circle( vec2 _st, vec2 _center, float _radius )
{
const float thickness = 20.0;
float dist = length(_st - _center);
return 1.0 - smoothstep(0.0, thickness/2.0, abs(_radius-dist));
}
void main()
{
vec2 cpt_p = (cpt.xy * 0.5 + 0.5) * u_resolution.xy * u_pixel_ratio;
float radius_p = radius * 0.5 * u_resolution.y * u_pixel_ratio.y;
float point = circle(gl_FragCoord.xy, cpt_p, radius_p);
gl_FragColor = vec4(point);
}