8
votes

I've written a GLSL shader for use in Unity3D with my pixelated iOS app. It has two ONE problems:

1) The effect doesn't always stay with the moon, and 2) the lighting doesn't look pixelated.

I'll explain a little. The shader is meant to cause a lighting effect on a foreground object when the moon is behind it. I have a sprite which is normally displayed as all black (like a silhouette), but when the moon object is close enough to it, it reveals the details of the texture, like so:

enter image description here

The actual sprite (without the shader) looks something like this:

enter image description here

The reason I am doing the effect this way is so that I can have a lightmap with only one texture. However, there are two problems with it:

1) As I get closer to the edge of the screen, the light effect doesn't follow the moon exactly. I'm not sure if this is related to my incorrect usage usage of the TEXCOORD1 variable. I've made a sprite that covers the whole screen so you can see how it doesn't follow the moon. Here's a picture of the problem in my test scene for the effect. There's a full-screen sprite with black stripes and alpha to show exactly where the effect is at all times. I want to get rid of this desync, because it makes the lighting look unrealistic.

enter image description here

2) The lighting effect caused is very sharp -- It would look better, I think, if each pixel was lit individually, as in SpriteLamp. I know it has to do with rounding the fragment coordinates in the shader, but since I never really know what the values of variables are in my shader, I can't seem to get this to work correctly.

I can round and floor the distance, but then I get this, which is obeying the distance from the moon, but not obeying the pixels in the original sprite. There is pixelation, but we end up with this weird curve across what should be just one pixel, if you catch my meaning...

enter image description here

What I'm looking for is more like this. See how the individual pixels of the original texture is lit?

enter image description here

Here's a link to the shader itself: http://pastebin.com/0zbqrP57. It's the default unity sprite shader, mostly, but with some code added in at the end of the SampleSpriteTexture function.

Here's the link to a unitypackage that you can use to look at the shader in action. Just try moving the moon around while the scene is playing.

https://drive.google.com/open?id=0BxCQjUHiAAQWZGpPZ3R2SExTcXc

MY QUESTION: Do you have any advice on how to solve these problems, or solutions to them?

1
I understand your problem, but you didn't make a specific question about it. Are you trying to understand why the desync happens? Expecting suggestions on workarounds? What exactly is the objective here? Please edit.XenoRo
Hi, @AlmightyR, thanks for the suggestion. I am expecting suggestions and solutions. I've updated the question. The goal is to implement the effect in problem #2 (as it will look better than the current setup), and solve the desync problem.Catlard
wow it works like a charm . thank youuser6517192
Haha, enjoy your broken shader! Come back when it's fixed.Catlard
@Catlard: to achieve pixelated lighting you should render at lower resolution and then upsample with nearest-neighbor.Yakov Galka

1 Answers

2
votes

Changing

OUT.worldSpacePosition = mul(_Object2World, OUT.vertex);

for

OUT.worldSpacePosition = mul(_Object2World, IN.vertex);

should fix moon misplacement.

For second problem I came up with this:

aS = floor(aS * 100) / 100;
bS = floor(bS * 100) / 100;
float dist = sqrt((aS * aS) + (bS * bS));
float percentClose = 1 - (dist / _LightStrength);
float rounded = floor(percentClose * 10) / 10;

if (color.r + color.g + color.b > 0) {
    color.r = lerp(0, color.r, rounded);
    color.g = lerp(0, color.g, rounded);
    color.b = lerp(0, color.b, rounded);
}

I think you can drop if statement and lerps to just:

aS = floor(aS * 100) / 100;
bS = floor(bS * 100) / 100;
float dist = sqrt((aS * aS) + (bS * bS));
float percentClose = 1 - (dist / _LightStrength);
float rounded = floor(percentClose * 10) / 10;
color.rgb *= rounded;

But in this case you should test it on your end device and check if it improves performance.