Unity3D shader bug on ios device - ios

I use water shader but it bug when build to ios device (still work fine on android, editor, mac).Normal
Bug on ios
Bug: Plane has shader alway on top of screen and plane has been upside down.
I already debug on device but nothing error ...
Here is shader code
Shader "RealisticWater/WaterMobile" {
Properties {
_Color ("Main Color", Color) = (1,1,1,1)
_ReflectionColor ("Reflection Color", Color) = (1,1,1,1)
_Wave1 ("Wave1 Distortion Texture", 2D) = "bump" {}
_Wave2 ("Wave2 Distortion Texture", 2D) = "bump" {}
_Cube("Reflection Map", Cube) = "" {}
_Direction ("Waves Direction 1 & 2", Vector) = (1.0 ,1.0, -1.0, -1.0)
_FPOW("FPOW Fresnel", float) = 5.0
_R0("R0 Fresnel", float) = 0.05
_OffsetFresnel("Offset Fresnel", float) = 0.1
_Distortion ("Distortion", float) = 500
_DistortionVert ("Per Vertex Distortion", Float) = 1
_GAmplitude ("Wave Amplitude", Vector) = (0.1 ,0.3, 0.2, 0.15)
_GFrequency ("Wave Frequency", Vector) = (0.6, 0.5, 0.5, 1.8)
_GSteepness ("Wave Steepness", Vector) = (1.0, 2.0, 1.5, 1.0)
_GSpeed ("Wave Speed", Vector) = (-0.23, -1.25, -3.0, 1.5)
_GDirectionAB ("Wave Direction", Vector) = (0.3 ,0.5, 0.85, 0.25)
_GDirectionCD ("Wave Direction", Vector) = (0.1 ,0.9, 0.5, 0.5)
_WaveScale("Waves Scale", float) = 1
_TexturesScale("Textures Scale", Float) = 1
}
Category {
Tags { "Queue"="Transparent+1" "RenderType"="Transparent" }
ZWrite Off
Cull Off
SubShader {
Tags { "Queue"="Transparent+1" "RenderType"="Transparent" }
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma fragmentoption ARB_precision_hint_fastest
#pragma glsl_no_auto_normalization
#pragma target 3.0
#pragma multi_compile cubeMap_on cubeMap_off
//#pragma glsl
#include "UnityCG.cginc"
fixed _WaveScale;
float _TexturesScale;
fixed4 _Color;
fixed4 _GlareColor;
fixed4 _ReflectionColor;
sampler2D _GrabTextureMobile;
fixed4 _GrabTextureMobile_TexelSize;
sampler2D _Wave1;
sampler2D _Wave2;
samplerCUBE _Cube;
fixed4 _Direction;
fixed _FPOW;
fixed _R0;
fixed _OffsetFresnel;
fixed _Distortion;
fixed _DistortionVert;
sampler2D _ReflectionTex;
fixed _Bias;
fixed _Scale;
fixed _Power;
fixed _Shininess;
fixed4 _LightColor0;
fixed4 _GAmplitude;
fixed4 _GFrequency;
fixed4 _GSteepness;
fixed4 _GSpeed;
fixed4 _GDirectionAB;
fixed4 _GDirectionCD;
fixed4 _Wave1_ST;
fixed4 _Wave2_ST;
fixed4 _Wave3_ST;
sampler2D _WaterDisplacementTexture;
struct appdata_t {
fixed4 vertex : POSITION;
fixed2 texcoord: TEXCOORD0;
fixed3 normal : NORMAL;
};
struct v2f {
fixed4 vertex : POSITION;
fixed4 uvgrab : TEXCOORD0;
fixed4 uvWave12 : TEXCOORD1;
fixed4 offset : TEXCOORD2;
#if cubeMap_on
fixed3 reflectionDir : TEXCOORD3;
#endif
};
v2f vert (appdata_t v)
{
v2f o;
fixed2 time1 = fixed2(fmod(_Time.x*_Direction.x, 1), fmod(_Time.x*_Direction.y, 1));
fixed2 time2 = fixed2(fmod(_Time.x*_Direction.z, 1), fmod(_Time.x*_Direction.w, 1));
fixed3 posWorld = mul(unity_ObjectToWorld, v.vertex).xyz;
fixed2 scaleeUv = -posWorld.xz / _TexturesScale;
o.uvWave12.xy = scaleeUv * _Wave1_ST.xy + _Wave1_ST.w + time1;
o.uvWave12.zw = scaleeUv * _Wave2_ST.xy + _Wave2_ST.w + time2;
//--------------------Gerstner waves-------------
fixed2 vtxForAni = posWorld.xz / _WaveScale;
fixed3 offsets;
fixed4 AB = _GSteepness.xxyy * _GAmplitude.xxyy * _GDirectionAB.xyzw;
fixed4 CD = _GSteepness.zzww * _GAmplitude.zzww * _GDirectionCD.xyzw;
fixed4 dotABCD = _GFrequency.xyzw * fixed4(dot(_GDirectionAB.xy, vtxForAni), dot(_GDirectionAB.zw, vtxForAni), dot(_GDirectionCD.xy, vtxForAni), dot(_GDirectionCD.zw, vtxForAni));
fixed4 TIME = fmod(_Time.y * _GSpeed, 6.2831);
fixed4 COS = cos (dotABCD + TIME);
fixed4 SIN = sin (dotABCD + TIME);
offsets.x = dot(COS, fixed4(AB.xz, CD.xz));
offsets.z = dot(COS, fixed4(AB.yw, CD.yw));
offsets.y = dot(SIN, _GAmplitude);
//------------------------------------------------
v.vertex.xyz += offsets;
fixed4 oPos = UnityObjectToClipPos(v.vertex);
#if UNITY_UV_STARTS_AT_TOP
fixed scale = -1.0;
#else
fixed scale = 1.0;
#endif
o.uvgrab.xy = (fixed2(oPos.x, oPos.y*scale) + oPos.w) * 0.5;
o.uvgrab.zw = oPos.zw;
o.uvgrab.xy += (offsets.xz + offsets.y*offsets.y)/_DistortionVert;
fixed3 normWorld = normalize(mul((fixed3x3)(unity_ObjectToWorld), v.normal).xyz);
#if cubeMap_on
fixed3 normalDir = normalize(mul(float4(v.normal, 0.0), unity_WorldToObject).xyz);
o.reflectionDir = reflect(posWorld - _WorldSpaceCameraPos, normalDir);
#endif
o.offset.xy = _Distortion*fixed2(0.001, 0.001);
o.offset.zw = o.offset.xy/30;
o.offset.xy = o.offset.xy*o.offset.xy*o.offset.xy;
o.vertex = UnityObjectToClipPos(v.vertex);
return o;
}
fixed4 frag( v2f i ) : COLOR
{
fixed2 normal1 = UnpackNormal(tex2D(_Wave1, i.uvWave12.xy)).rg;
fixed2 normal2 = UnpackNormal(tex2D(_Wave2, i.uvWave12.zw + normal1)).rg;
fixed2 offset = normal2 * normal2 * normal2 * i.offset.xy + normal2 * i.offset.zw;
i.uvgrab.xy = offset * i.uvgrab.z + i.uvgrab.xy;
#if UNITY_VERSION >= 460
fixed4 coord = UNITY_PROJ_COORD(i.uvgrab);
#else
fixed4 coord = i.uvgrab;
#endif
fixed3 reflection;
#if cubeMap_off
reflection = tex2Dproj(_ReflectionTex, coord).rgb * _ReflectionColor.rgb;
#endif
#if cubeMap_on
reflection = texCUBE(_Cube, i.reflectionDir) * _ReflectionColor.rgb;
#endif
fixed3 grab = tex2Dproj(_GrabTextureMobile, coord).rgb;
fixed3 col = grab * _Color.rgb + reflection + reflection * offset.x * 10;
return fixed4(col, 1);
}
ENDCG
}
}
// ------------------------------------------------------------------
// Fallback for older cards and Unity non-Pro
SubShader {
Blend DstColor Zero
Pass {
Name "BASE"
SetTexture [_MainTex] { combine texture }
}
}
}
}
Sorry if my english not good. Thanks you guys !

Related

Adding Light Falloff for multiple Point Lights

I'm currently trying to add multiple point lights to my game. What I have done appears to be mostly working, except for a small problem of blending light falloff. Here's two images to show you what's happening. In the first one, Light Falloff is commented out. Both point lights appear correctly.
And here's the second image, where I have light falloff enabled. You will see that only light #2 is "mostly" visible. There are traces of light #1, but for the most part, light #1 appears to be overridden by light #2's falloff. In other words, each consecutive light's falloff overrides the light from previous lights.
Does anyone know how to add falloff for multiple point lights? I'm sure I'm doing something slightly wrong, and that's why the lights are not properly accumulated.
Here's my shader:
struct Vertex
{
float4 pos : POSITION;
float2 tex : TEXTURE;
float3 norm : NORMAL;
};
struct PixelShaderArgs
{
float4 pos : SV_POSITION;
float2 col : TEXTURE;
float3 norm : NORMAL;
float3 worldPos : POSITION;
};
struct PointLightShaderArgs
{
float3 pos;
float radius;
float intensity;
float3 padding;
float4 ambient;
float4 diffuse;
};
Texture2D ShaderTexture : register(t0);
SamplerState Sampler : register(s0);
float4x4 localMatrix : register(b0);
cbuffer ShaderDataBuffer : register(b1)
{
float2 TextureResolution;
};
cbuffer cbPerFrame : register(b3)
{
PointLightShaderArgs light[8];
};
cbuffer WorldPositionBuffer : register(b4)
{
float4x4 World;
};
PixelShaderArgs VertexShaderMain(Vertex vertex)
{
PixelShaderArgs output;
output.pos = mul(vertex.pos, localMatrix);
output.col = vertex.tex;
output.norm = mul(vertex.norm, World);
output.worldPos = mul(vertex.pos, World);
return output;
}
int2 convertUVToPixel(float u, float v)
{
int width = TextureResolution.x;
int height = TextureResolution.y;
int xCoordinate = floor(u * width);
int yCoordinate = floor(v * height);
return int2(xCoordinate % width, yCoordinate % height);
}
float Falloff(float distance, float radius)
{
return clamp(1.0f - (distance / radius), 0.0, 1.0);
}
#define ATTENUATION_CONSTANT 1.0f // 0% Constant
#define ATTENUATION_LINEAR 0.0f // 100% Linear
#define ATTENUATION_QUADRATIC 0.0f // 100% Quadratic
float4 PixelShaderMain(PixelShaderArgs pixelShaderArgs) : SV_Target
{
float u = pixelShaderArgs.col.x;
float v = pixelShaderArgs.col.y;
// Lighting
float3 fragColor = float3(0.0f, 0.0f, 0.0f);
float4 diffuse = ShaderTexture.Load(int3(convertUVToPixel(u, v), 0));
for (int i = 0; i < 2; i++)
{
float3 ambient = diffuse * light[i].ambient;
pixelShaderArgs.norm = normalize(pixelShaderArgs.norm);
float3 lightToPixelVec = light[i].pos - pixelShaderArgs.worldPos;
float distance = length(lightToPixelVec);
float luminosity = dot(lightToPixelVec / distance, pixelShaderArgs.norm);
float intensity = 1.00f;
if (luminosity > 0.0f)
{
// Do lighting attenuation
fragColor += luminosity * diffuse * light[i].diffuse;
fragColor /= ATTENUATION_CONSTANT + (ATTENUATION_LINEAR * distance) + (ATTENUATION_QUADRATIC * (distance * distance));
fragColor *= light[i].intensity; // multiply the final result by the intensity.
fragColor *= Falloff(distance, light[i].radius); // This is what's causing the problem!!
//fragColor = saturate(fragColor + ambient);
}
}
return float4(fragColor, diffuse.a);
}
I figured this out. The solution was to move the falloff calculation up and inline it with the following line: fragColor += luminosity * diffuse * light[i].diffuse * Falloff(distance,light[i].radius);
This results the correcting falloff blending, shown in this picture:
and another picture showing three overlapped point lights:
Here's the updated shader (A lot of changes were made from the first one because I'm actually posting this answer late)
struct Vertex
{
float4 pos : POSITION;
float2 tex : TEXTURE;
float3 norm : NORMAL;
};
struct PixelShaderArgs
{
float4 pos : SV_POSITION;
float2 col : TEXTURE;
float3 norm : NORMAL;
float3 worldPos : POSITION;
};
struct PointLightShaderArgs
{
float3 pos;
float radius;
float intensity;
float3 padding;
float4 ambient;
float4 diffuse;
};
Texture2D ShaderTexture : register(t0);
SamplerState Sampler : register(s0);
float4x4 localMatrix : register(b0);
cbuffer ShaderDataBuffer : register(b1)
{
float2 TextureResolution;
};
cbuffer cbPerFrame : register(b3)
{
PointLightShaderArgs light[32];
};
cbuffer WorldPositionBuffer : register(b4)
{
float4x4 World;
};
PixelShaderArgs VertexShaderMain(Vertex vertex)
{
PixelShaderArgs output;
output.pos = mul(vertex.pos, localMatrix);
output.col = vertex.tex;
output.norm = mul(vertex.norm, World);
output.worldPos = mul(vertex.pos, World);
return output;
}
int2 convertUVToPixel(float u, float v)
{
int width = TextureResolution.x;
int height = TextureResolution.y;
int xCoordinate = floor(u * width);
int yCoordinate = floor(v * height);
return int2(xCoordinate % width, yCoordinate % height);
}
float Falloff(float distance, float radius)
{
return clamp(1.0f - (distance / radius), 0.0, 1.0);
}
#define ATTENUATION_CONSTANT 1.0f // 0% Constant
#define ATTENUATION_LINEAR 0.0f // 100% Linear
#define ATTENUATION_QUADRATIC 0.0f // 100% Quadratic; Democrats are domestic terrorists
float4 PixelShaderMain(PixelShaderArgs pixelShaderArgs) : SV_Target
{
float u = pixelShaderArgs.col.x;
float v = pixelShaderArgs.col.y;
// Lighting
float3 fragColor = float3(0.0f, 0.0f, 0.0f);
float4 diffuse = ShaderTexture.Load(int3(convertUVToPixel(u, v), 0));
for (int i = 0; i < 32; i++)
{
float3 ambient = diffuse * light[i].ambient;
pixelShaderArgs.norm = normalize(pixelShaderArgs.norm);
float3 lightToPixelVec = light[i].pos - pixelShaderArgs.worldPos;
float distance = length(lightToPixelVec);
float luminosity = dot(lightToPixelVec / distance, pixelShaderArgs.norm);
float intensity = 1.00f;
if (luminosity > 0.0f)
{
// Do lighting attenuation
fragColor += luminosity * diffuse * light[i].diffuse * Falloff(distance,light[i].radius);
fragColor /= ATTENUATION_CONSTANT + (ATTENUATION_LINEAR * distance) + (ATTENUATION_QUADRATIC * (distance * distance));
fragColor *= light[i].intensity; // multiply the final result by the intensity.
}
fragColor = saturate(fragColor + ambient);
}
return float4(fragColor, diffuse.a);
}

Fragment shader and Vertex Shader in Metal

Fragment Shader
vertex VertexOutBezier bezier_vertex(constant BezierParameters *allParams[[buffer(0)]],
// constant GlobalParameters& globalParams[[buffer(1)]],
uint vertexId [[vertex_id]],
uint instanceId [[instance_id]])
{
float t = (float) vertexId / 300;
rint(t);
BezierParameters params = allParams[instanceId];
float lineWidth = (1 - (((float) (vertexId % 2)) * 2.0)) * params.lineThickness;
float2 a = params.a;
float2 b = params.b;
float nt = 1.0f - t;
float nt_2 = nt * nt;
float nt_3 = nt_2 * nt;
float t_2 = t * t;
float t_3 = t_2 * t;
float2 point = a * nt_3 + params.p1 * nt_2 * t + params.p2 * nt * t_2 + b * t_3;
float2 tangent = -3.0 * a * nt_2 + params.p1 * (1.0 - 4.0 * t + 3.0 * t_2) + params.p2 * (2.0 * t - 3.0 * t_2) + 3 * b * t_2;
tangent = normalize(float2(-tangent.y, tangent.x));
VertexOutBezier vo;
vo.pos.xy = point + (tangent * (lineWidth / 3.0f));
vo.pos.zw = float2(0, 1);
vo.color = params.color ;
return vo;
}
My Fragment shader is
fragment float4 bezier_fragment(VertexOutBezier params[[stage_in]],
texture2d<float> texture [[texture(0)]]
)
{
constexpr sampler defaultSampler;
float4 canvasColor = texture.sample(defaultSampler, params.pos.xy);
return canvasColor;
}
Here i expect to get the pixel color of the texture. But here it is only getting single color. It is not getting the color of the texture according to its position.
Even when I do this in fragment I am getting the single color it is not varying with coordinates
fragment float4 bezier_fragment(VertexOutBezier params[[stage_in]],
texture2d<float> texture [[texture(0)]]
)
{
constexpr sampler defaultSampler;
float4 canvasColor = params.color * params.pos.x;
return canvasColor;
}
If I do this in Vertex Shader I got color varying according position of x
vo.pos.xy = point + (tangent * (lineWidth / 3.0f));
vo.pos.zw = float2(0, 1);
vo.color = params.color * vo.pos.x;
What is the Issue in fragment Shader. I cannot get the coordinates from Vertex Shader
Please make sure the VertexOutBezier.pos.xy value is normalization ( 0 ~ 1.0) ,due to the defaultSampler only receive normalization position value, if always return a single may be the position is beyond the bounds.

Area Light Implementation with directx9 or DX11

I want to implement AreaLight Using Directx 9 Plz help me that how I can start. I Studies Two three link but enable to implement in RenderMonkey or Direct With Sample
Spherical area light HLSL
float specTrowbridgeReitz(float HoN,float a,float aP)
{
float a2 = a*a;
float ap2 = aP*aP;
return ( a2 * ap2 ) / pow( HoN * HoN * ( a2 - 1.0 ) + 1.0, 2.0 );
}
float visSchlickSmithMod( float NoL, float NoV, float r )
{
float k = pow( r * 0.5 + 0.5, 2.0 ) * 0.5;
float l = NoL * ( 1.0 - k ) + k;
float v = NoV * ( 1.0 - k ) + k;
return 1.0 / ( 4.0 * l * v );
}
float fresSchlickSmith( float HoV, float f0 )
{
return f0 + ( 1.0 - f0 ) * pow( 1.0 - HoV, 5.0 );
}
float sphereLight(float3 pos,float3 N,float3 V,float3 r,float f0,float
roughness,float NoV,out float NoL)
{
float3 L = lightPositions - pos;
float3 centerToRay = dot(L,r)*r-L;
float3 closestPoint = L + centerToRay * clamp( fLightRadius / length(
centerToRay ), 0.0, 1.0 );
float3 l = normalize(closestPoint);
float3 h = normalize(V+l);
NoL = clamp(dot(N,l),0.0,1.0);
float HoN = clamp(dot(h,N),0.0,1.0);
float HoV = dot(h,V);
float distL = length(L);
float alpha = roughness * roughness;
float alphaPrime = clamp(fLightRadius/(distL*2.0)+alpha,0.0,1.0);
float specD = specTrowbridgeReitz( HoN, alpha, alphaPrime );
float specF = fresSchlickSmith( HoV, f0 );
float specV = visSchlickSmithMod( NoL, NoV, roughness );
return specD * specF * specV*NoL;
}
float3 areaLights(float3 pos,float3 nor,float3 V)
{
float f0 = FO;
float NoV = clamp(dot(nor,V),0.0,1.0);
float3 r = reflect(-V,nor);
float NdotLSphere;
float specSph = sphereLight(pos,nor,V,r,f0,roughness,NoV,NdotLSphere);
float3 color = 0.3183*(NdotLSphere*lightcolor)+
(specSph*lightcolor)+albedo;
return pow(color,1.0/2.2);
}
float4 ps_main( PS_INPUT Input ) : COLOR0
{
float3 N = normalize(Input.Normal);
float3 viewDir = normalize(campos.xyz -
float3(Input.WSPosition.xyz));
float3 color = areaLights(Input.WSPosition,N,viewDir);
return float4(color,0.0);
}

how do I port this Shadertoy shader with an fwidth() call to a Metal shader?

I've been porting Shadertoy shaders to Metal in order to learn how to write Metal shaders. I don't think I'm doing it correctly as I have been writing every one of my shaders as a compute shader, rather than vertex/fragment shaders. This has worked for quite a few shaders I've ported, almost 20. However some ports are extremely slow, and others include functions that aren't available.
Here is one of the shaders that is tripping me up:
https://www.shadertoy.com/view/4t2SRh
The fwidth() call in render() and mainImage() is not allowed within a metal compute shader. Metal Shader Language does however have fwidth(), but it can only be called within a fragment shader.
Here is my attempt at porting to a compute shader:
#include <metal_stdlib>
using namespace metal;
float float_mod(float f1, float f2) {
return f1-f2 * floor(f1/f2);
}
float sdfCircle(float2 center, float radius, float2 coord )
{
float2 offset = coord - center;
return sqrt((offset.x * offset.x) + (offset.y * offset.y)) - radius;
}
float sdfEllipse(float2 center, float a, float b, float2 coord)
{
float a2 = a * a;
float b2 = b * b;
return (b2 * (coord.x - center.x) * (coord.x - center.x) +
a2 * (coord.y - center.y) * (coord.y - center.y) - a2 * b2)/(a2 * b2);
}
float sdfLine(float2 p0, float2 p1, float width, float2 coord)
{
float2 dir0 = p1 - p0;
float2 dir1 = coord - p0;
float h = clamp(dot(dir0, dir1)/dot(dir0, dir0), 0.0, 1.0);
return (length(dir1 - dir0 * h) - width * 0.5);
}
float sdfUnion( const float a, const float b )
{
return min(a, b);
}
float sdfDifference( const float a, const float b)
{
return max(a, -b);
}
float sdfIntersection( const float a, const float b )
{
return max(a, b);
}
float anti(float d) {
return fwidth(d) * 1.0;
}
float4 render(float d, float3 color, float stroke)
{
//stroke = fwidth(d) * 2.0;
float anti = fwidth(d) * 1.0;
float4 strokeLayer = float4(float3(0.05), 1.0-smoothstep(-anti, anti, d - stroke));
float4 colorLayer = float4(color, 1.0-smoothstep(-anti, anti, d));
if (stroke < 0.000001) {
return colorLayer;
}
return float4(mix(strokeLayer.rgb, colorLayer.rgb, colorLayer.a), strokeLayer.a);
}
kernel void compute(texture2d<float, access::write> output [[texture(0)]],
texture2d<float, access::sample> input [[texture(1)]],
constant float &timer [[buffer(0)]],
uint2 gid [[thread_position_in_grid]])
{
float4 fragColor;
int width = output.get_width();
int height = output.get_height();
float2 resolution = float2(width,height);
float2 uv = float2(gid) / resolution;
float size = min(resolution.x, resolution.y);
float pixSize = 1.0 / size;
float stroke = pixSize * 1.5;
float2 center = float2(0.5, 0.5 * resolution.y/resolution.x);
float a = sdfEllipse(float2(0.5, center.y*2.0-0.34), 0.25, 0.25, uv);
float b = sdfEllipse(float2(0.5, center.y*2.0+0.03), 0.8, 0.35, uv);
b = sdfIntersection(a, b);
float4 layer1 = render(b, float3(0.32, 0.56, 0.53), fwidth(b) * 2.0);
// Draw strips
float4 layer2 = layer1;
float t, r0, r1, r2, e, f;
float2 sinuv = float2(uv.x, (sin(uv.x*40.0)*0.02 + 1.0)*uv.y);
for (float i = 0.0; i < 10.0; i++) {
t = float_mod(timer + 0.3 * i, 3.0) * 0.2;
r0 = (t - 0.15) / 0.2 * 0.9 + 0.1;
r1 = (t - 0.15) / 0.2 * 0.1 + 0.9;
r2 = (t - 0.15) / 0.2 * 0.15 + 0.85;
e = sdfEllipse(float2(0.5, center.y*2.0+0.37-t*r2), 0.7*r0, 0.35*r1, sinuv);
f = sdfEllipse(float2(0.5, center.y*2.0+0.41-t), 0.7*r0, 0.35*r1, sinuv);
f = sdfDifference(e, f);
f = sdfIntersection(f, b);
float4 layer = render(f, float3(1.0, 0.81, 0.27), 0.0);
layer2 = mix(layer2, layer, layer.a);
}
// Draw the handle
float bottom = 0.08;
float handleWidth = 0.01;
float handleRadius = 0.04;
float d = sdfCircle(float2(0.5-handleRadius+0.5*handleWidth, bottom), handleRadius, uv);
float c = sdfCircle(float2(0.5-handleRadius+0.5*handleWidth, bottom), handleRadius-handleWidth, uv);
d = sdfDifference(d, c);
c = uv.y - bottom;
d = sdfIntersection(d, c);
c = sdfLine(float2(0.5, center.y*2.0-0.05), float2(0.5, bottom), handleWidth, uv);
d = sdfUnion(d, c);
c = sdfCircle(float2(0.5, center.y*2.0-0.05), 0.01, uv);
d = sdfUnion(c, d);
c = sdfCircle(float2(0.5-handleRadius*2.0+handleWidth, bottom), handleWidth*0.5, uv);
d = sdfUnion(c, d);
float4 layer0 = render(d, float3(0.404, 0.298, 0.278), stroke);
float2 p = (2.0*float2(gid).xy-resolution.xy)/min(resolution.y,resolution.x);
float3 bcol = float3(1.0,0.8,0.7-0.07*p.y)*(1.0-0.25*length(p));
fragColor = float4(bcol, 1.0);
fragColor.rgb = mix(fragColor.rgb, layer0.rgb, layer0.a);
fragColor.rgb = mix(fragColor.rgb, layer1.rgb, layer1.a);
fragColor.rgb = mix(fragColor.rgb, layer2.rgb, layer2.a);
fragColor.rgb = pow(fragColor.rgb, float3(1.0/2.2));
output.write(fragColor,gid);
}
This doesn't compile, as fwidth() is not available. However, if I do get rid of fwidth(), it will compile... but of course not draw the right thing.
I was wondering if there is a better way to port this to a fragment/vertex shader, so that I can use MSL's fwidth() ? Or is writing it as a compute shader fine, and I should find a different way around using fwidth() ?

Custom CIKernel Displacement Map

I'm trying to create a displacement map cikernel for iOS 8 that shifts the pixels horizontally from the map R channel and vertically from the G channel.
The map pixel coordinates must be picked relative to the source image size mapPixel = ((dest.x/source.width) * map.width, (dest.y / source.height) * map.height)
The input image size that I test with is 2048 x 2048
and the map is red-green perlin noise 2560 x 2560
In Quartz Composer the cikernel works almost as expected, except that the map is not applied to the whole image
kernel vec4 coreImageKernel(sampler image, sampler displaceMap, float scaleX, float scaleY)
{
vec2 destination = destCoord();
vec2 imageSize = samplerSize(image);
float xPercent = destination.x / imageSize.x;
float yPercent = destination.y / imageSize.y;
vec2 mapSize = samplerSize(displaceMap);
vec2 mapCoord = vec2(mapSize.x * xPercent, mapSize.y * yPercent);
vec4 mapPixel = sample(displaceMap, mapCoord);
float ratioShiftX = ((mapPixel.x) * 2.0) - 1.0;
float ratioShiftY = ((mapPixel.y) * 2.0) - 1.0;
vec2 pixelShift = vec2(ratioShiftX * scaleX, ratioShiftY * scaleY);
return sample(image, destination - pixelShift);
}
Here's what the filter function looks like:
function __image main(__image image, __image displaceMap, __number scaleX, __number scaleY) {
return coreImageKernel.apply(image.definition, null, image, displaceMap, scaleX, scaleY);
}
But when I load the cikernel in CIFilter the result is far from what I see in Quartz Composer.
Here's what my apply function looks like in the CIFilter
override var outputImage:CIImage? {
if let inputImage = inputImage {
if let inputMap = inputMap {
let args = [inputImage as AnyObject, inputMap as AnyObject, inputScaleX, inputScaleY]
return CIDisplacementMapFilter.kernel?.applyWithExtent(inputImage.extent, roiCallback: {
(index, rect) in
if index == 0 {
return rect
}
return CGRectInfinite
}, arguments: args)
}
}
return nil
}
I'm guessing the ROI is wrong and the sampler is tiled, but I can't figure it out.
As it turns out the kernel was wrong.
Here's a kernel that does the job
kernel vec4 displace(sampler source, sampler map, float scaleX, float scaleY)
{
vec2 d = destCoord();
vec4 mapPixel = sample(map, samplerTransform(map, d));
float shiftX = ((mapPixel.x * 2.0) - 1.0) * scaleX;
float shiftY = ((mapPixel.y * 2.0) - 1.0) * scaleY;
vec2 s = samplerTransform(source, d + vec2(shiftX, shiftY));
return sample(source, s);
}
This is the same code for Metal
#include <metal_stdlib>
using namespace metal;
#include <CoreImage/CoreImage.h>
extern "C" {
namespace coreimage {
float4 displaceFilterKernel(sampler source, sampler map, float scaleX, float scaleY)
{
float2 d = map.coord();
float4 mapPixel = map.sample(d);
float shiftX = ((mapPixel.x * 2.0) - 1.0) * scaleX;
float shiftY = ((mapPixel.y * 2.0) - 1.0) * scaleY;
float2 s = float2(d.x, 1.0 - d.y) + float2(shiftX, shiftY);
return sample(source, s);
}
}
}

Resources