Существует два подхода к этой проблеме - либо вы передаете координату текстуры и пытаетесь преобразовать ее в мировое пространство внутри шейдера, либо вы переходите в мировую позицию и сравниваете ее с мировой позицией фрагмента. Последнее, без сомнения, самое простое.
Итак, допустим, что вы передаете мировую позицию в шейдер следующим образом:
drawMaterial.SetVector("_Coordinate", new Vector4(hit.point.x, hit.point.y, hit.point.z, 0));
Расчет мировой позиции для фрагмента стоит дорого, поэтому мы делаем это внутри вершинного шейдера и позвольте аппаратному обеспечению интерполировать значение на фрагмент. Давайте добавим мировую позицию в нашу структуру v2f:
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
float3 worldPos : TEXCOORD1;
};
Чтобы вычислить мировую позицию внутри вершинного шейдера, мы можем использовать встроенную матрицу unity_ObjectToWorld:
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
return o;
}
Наконец, мы можем получить доступ к значению в фрагментном шейдере следующим образом:
float draw =pow(saturate(1-distance(i.worldPos,_Coordinate.xyz)),100);
РЕДАКТИРОВАТЬ: Я только что понял - когда вы делаете проход блит, вы не рендеринг со своим sh, вы рендеринг в квад, который покрывает весь экран. Из-за этого, когда вы вычисляете расстояние до вершины, вы получаете расстояние до углов экрана, что не правильно. Хотя есть способ решить эту проблему - вы можете изменить цель рендеринга на вашу текстуру рендеринга и нарисовать me sh, используя шейдер, который проецирует me sh UV на экран.
Это немного Это сложно объяснить, но в основном способ работы вершинных шейдеров заключается в том, что вы берете вершину, которая находится в локальном объектном пространстве, и трансформируете ее так, чтобы она была относительно экрана в пространстве от -1 до 1 по обеим осям, где 0 находится в центр. Это называется нормализованным пространством координат устройства или ND C. Мы можем использовать это, чтобы сделать так, чтобы вместо использования матриц модели и камеры для преобразования наших вершин мы использовали координаты UV, преобразованные из пространства [0,1] в [-1,1]. В то же время мы можем рассчитать нашу мировую позицию и передать ее на фрагмент отдельно. Вот как будет выглядеть шейдер:
v2f vert (appdata v)
{
v2f o;
float2 uv = v.texcoord.xy;
// https://docs.unity3d.com/Manual/SL-PlatformDifferences.html
if (_ProjectionParams.x < 0) {
uv.y = 1 - uv.y;
}
// Convert from 0,1 to -1,1, for the blit
o.vertex = float4(2 * (uv - 0.5), 0, 1);
// We still need UVs to draw the base texture
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
// Let's do the calculations in local space instead!
o.localPos = v.vertex.xyz;
return o;
}
Также не забудьте передать переменную _Coordinate в локальном пространстве, используя transform.InverseTransformPoint.
Теперь нам нужно использовать другой подход для рендеринга в текстуру рендеринга. По сути, нам нужно визуализировать фактическое me sh, как если бы мы выполняли рендеринг с камеры - за исключением того, что это me sh будет отображаться в виде развернутого УФ-листа на экране. Сначала мы устанавливаем активную текстуру рендеринга для текстуры, в которую мы хотим нарисовать:
// Cache the old target so that we can reset it later
RenderTexture previousRT = RenderTexture.active;
RenderTexture.active = temp;
(Вы можете прочитать о том, как работают цели рендеринга здесь ) Далее, нам нужно связать нашу материал и нарисуйте меня sh.
Material mat = drawMaterial;
Mesh mesh = yourAwesomeMesh;
mat.SetTexture("_MainTex", splatMap);
mat.SetPass(0); // This tells the renderer to use pass 0 from this material
Graphics.DrawMeshNow(mesh, Vector3.zero, Quaternion.identity);
Наконец, верните текстуру обратно к оригиналу:
// Remember to reset the render target
RenderTexture.active = previousRT;
Graphics.Blit(temp, splatMap);
Я не проверял и не проверял это, но я использовал похожую технику, чтобы нарисовать меня sh в УФ раньше. Вы можете прочитать больше о DrawMeshNow здесь .