Писать впереди
Сегодня я попытаюсь обвести круг на веб-сайтах Unity и ShaderToy соответственно, чтобы увидеть конкретную разницу между ними.
Некоторая подготовительная работа перед запуском Unity и shadertoy будет пропущена, а код и эффект отображения будут загружены напрямую.
Часть единства
Использование Unity + VS для написания шейдеров - мое любимое, интуитивно понятное и быстрое, и вы также можете изменить эффект настройки параметров прямо на панели, которая намного выше, чем на веб-странице! Вставьте код шейдера ниже
Shader "Custom/circle" {
Properties {
//xy表示圆心在屏幕中的uv值,z为半径,w为圆边缘的平滑值
_parameters("circleParameter",Vector)=(0.5,0.5,10,0)
_Color("circleColor",COLOR)=(1,1,1,1)
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 200
Pass{
CGPROGRAM
#include "UnityCG.cginc"
#pragma fragmentoption ARB_precision_hint_fastest
#pragma target 3.0
#pragma vertex vert
#pragma fragment frag
#define vec2 float2
#define vec3 float3
#define vec4 float4
#define mat2 float2
#define mat3 float3
#define mat4 float4
#define iGlobalTime _Time.y
#define mod fmod
#define mix lerp
#define fract frac
#define Texture2D tex2D
#define iResolution _ScreenParams
float4 _parameters;
float4 _Color;
float4 _backgroundColor;
struct v2f{
float4 pos:SV_POSITION;
float4 srcPos:TEXCOORD0;
};
v2f vert(appdata_base v){
v2f o;
o.pos=mul(UNITY_MATRIX_MVP,v.vertex);
o.srcPos=ComputeScreenPos(o.pos);
return o;
}
vec4 main(vec2 fragCoord);
float4 frag(v2f iParam):COLOR{
//获取uv对应的当前分辨率下的点 uv范围(0-1) 与分辨率相乘
vec2 fragCoord=((iParam.srcPos.xy/iParam.srcPos.w)*_ScreenParams.xy);
return main(fragCoord);
}
//要先定义方法声明才能使用
vec4 cicle(vec2 pos,vec2 center,float radius,float3 col,float antialias){
//求出点到圆心距离,如果为正则在圆外 负在圆内 我们需要对圆内的点进行上色 即对负值进行处理
float d=length(pos-center)-radius;
//判断d的大小 如果小于0则返回0 如果大于antialias返回1 返回值在0-1之间
//smoothstep(a,b,t) 判断t t<a返回0,t>b返回1,t在a-b之间反差值返回0-1
float t=smoothstep(0,antialias,d);
//返回颜色值 在圆外的设置alpha=0透明
return vec4(col,1.0-t);
}
vec4 main(vec2 fragCoord){
vec2 pos=fragCoord;
//给背景一个动态的颜色
vec3 temp = 0.5 + 0.5*cos(iGlobalTime+pos.xyx/_ScreenParams.y+vec3(0,2,4));
//获取背景的颜色
vec4 layer1=vec4(temp,1.0);
//获取圆
vec4 layer2=cicle(pos,_parameters.xy*iResolution.xy,_parameters.z,_Color.rgb,_parameters.w);
//插值处理,使边界更模糊化,layer2中的_parameters.w值越大越模糊
return mix(layer1,layer2,layer2.a);
}
ENDCG
}
}
FallBack "Diffuse"
}
Код выглядит как очень длинный и очень длинный раздел, но на самом деле это шаблон для шейдера, игрушки для шейдера. Описание шаблона можно посмотреть здесь . Код ключа - это функция круга для рисования круга. Общий принцип shadertoy состоит в том, чтобы рисовать слои один за другим, а затем смешивать их с различными эффектами, что похоже на рисование PS. Он знает, почему чем лучше эффект, тем более застрявшим выглядит веб-страница shadetoy, а я не не знаю, сколько слоев уложено.
Чтобы сделать эффект Unity таким же, как и на стороне shadertoy, специально добавлен переменный цвет фона по умолчанию. Код выглядит следующим образом
//给背景一个动态的颜色
vec3 temp = 0.5 + 0.5*cos(iGlobalTime+pos.xyx/_ScreenParams.y+vec3(0,2,4));
Используя функцию косинуса с постоянно меняющимся значением времени iGlobalTime, фон может непрерывно менять цвет. Эффект запуска Unity выглядит следующим образом
Раздел ShaderToy
Я думал, что на shadertoy это будет сделано быстро. В конце концов, Unity написала кучу определенных кодов для совместимости с shadertoy. Я не ожидал найти грамматические ошибки в течение получаса, и это все та же ошибка, как будто я ' м вернулся к изучению программирования. . . Содержание ошибки следующее
Не пугайтесь этих красных прямоугольников, это две ошибки по одной и той же причине, неверный тип параметра. В shadertoy, пока вы определяете тип с плавающей точкой, параметр должен быть типом с плавающей запятой, в противном случае вы не найдете напрямую соответствующей перегруженной функции. Когда я сообщил об ошибке, я не осознал проблему. Я подумал, что это обычная встроенная функция. Почему она внезапно сообщила, что нет метода перегрузки? На время я пошел читать код, написанный другим люди, но я все еще не нашел проблемы., В конце концов, я не знаю, почему я изменил 1.0-t возвращаемого значения на 1-t. Красный цвет всего содержимого ошибки только разбудил меня, это Оказалось, что тип параметра неправильный! ! ! ! Метод smoothstep должен быть с плавающей запятой! ! Не поможет конвертировать! ! ! Даже если вам 0, вы должны написать 0,0! ! !
Ниже приведен неправильный код shadertoy.
float t=smoothstep(0,antialias,d);
Ниже приведен правильный код shadertoy.
float t=smoothstep(0.0,antialias,d);
Тихо. . . Строгий. . . Вставьте код shadertoy ниже
//输入参数(当前点位置,中心点位置,点的半径,颜色,与背景过渡的平滑值)
vec4 cicle(vec2 pos,vec2 center,float radius,vec3 col,float antialias){
//求圆心距离
float d=length(pos-center)-radius;
//smoothstep(a,b,t)函数 t<a return a, t>b return b
float t=smoothstep(0.0,antialias,d);
return vec4(col,1.0-t);
}
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
//获取点的位置
//iResolution为屏幕的分辨率
//fragCoord为当前点的位置 原点是左下角
//返回的uv是以屏幕中心为原点
vec2 uv =(2.0*fragCoord.xy-iResolution.xy) /iResolution.y;
//中心点
vec2 point1 = vec2(0,0);
//圆的颜色
vec3 color=vec3(1,0,0);
// layer1 cos函数
vec3 temp = 0.5 + 0.5*cos(iTime+uv.xyx+vec3(0,2,4));
vec4 layer1= vec4(temp,1);
//layer2 平滑的圆
vec4 layer2 = cicle(uv,point1,0.8,color,0.03);
// 输出像素
fragColor = mix(layer1,layer2,layer2.a);
}
Очень простой эффект, принципиально отличный от единства, но писать раскраску плавно в браузере действительно неудобно! ! webGL действительно легко сломать! ! Да и входные параметры изменить не очень-то просто. . . Еще раз осознаем удобство единства. .
подводить итоги
Вот некоторые различия между шейдером и кодом шейдера в Unity.
1. Тип символа на shadetoy не будет автоматически изменяться. Если в определяемой вами функции есть параметр типа float, то при использовании типа int при вызове обязательно будет сообщено об ошибке; значение 0 - целое число, а 0,0 - тип символа. (Unity пытался принудительно передать тип, нет проблем)
2. Входные позиции двух шейдеров различаются.
На вход фрагментной функции frag () кода шейдера в единице поступают координаты проекции вершин, которые необходимо нормализовать, а затем умножить на разрешение экрана для получения соответствующих координатных точек.
//(iParam.srcPos.xy/iParam.srcPos.w)获取归一化的点 范围在(0-1)
//与屏幕分辨率相乘获得实际的像素坐标,坐标原点在正中心
vec2 fragCoord=((iParam.srcPos.xy/iParam.srcPos.w)*_ScreenParams.xy);
Входными данными основного метода в shadertoy является позиция пикселя, соответствующая вершине. Начало координат - это нижний левый угол экрана. Его необходимо преобразовать в начало координат в центре. Эта операция преобразования исходного диапазона из (0 , a) до (-b, b) является исходным Положение координаты станет y = bx-b (0 <x <a). Но таким образом значение операнда будет увеличено во много раз, поэтому требуется единое масштабирование, а полученная координата положения делится на ось экрана.
//获取点的位置
//iResolution为屏幕的分辨率
//fragCoord为当前点的位置 原点是左下角
//返回的uv是以屏幕中心为原点 并且除以分辨率的一个轴进行缩放
vec2 uv =(2.0*fragCoord.xy-iResolution.xy) /iResolution.y;
PS: Непросто описать то, что вы понимаете. . .