简体   繁体   中英

Unity 2D Planet / Curved World Shader

I have been trying to create a 2D "fake planet" kind of effect using shaders, but the result I have, while it works, is not great.

void vert(inout appdata_full v)
{
    //Get vertex world coordinates
    float4 worldV = mul(unity_ObjectToWorld, v.vertex);

    //Get target coordinates relative to vertex world
    worldV.xyz -= _TargetPos.xyz;

    //Transform vertex based on x distance from target
    worldV = float4(0.0f, (worldV.x * worldV.x) * -_Curvature, 0.0f, 0.0f);

    //Add this offset to vertex
    v.vertex += mul(unity_WorldToObject, worldV);
}

The code above is the vertex function that I am using to produce this effect, but since it only offsets the vertices on the Y based on the player's X, it produces a distortion near the edge of the screen. It just looks off, since all vertical lines stay completely vertical.

What I would like it to do is make it appear more like a planet:

差异图像

But I'm not sure how I would go about this.

EDIT: I have been trying for hours to figure this out, and I thought I had figured it out:

计算/图表(涂鸦) .

But no, this did not work. The level just seemed to curve and dip in random places.

Try this:

float dist = sqrt((vv.x * vv.x) + (vv.z * vv.z));
vv = float4(0.0f, dist * dist * -_Curvature , 0.0f, 0.0f);

also you can add offset:

float dist = sqrt((vv.x * vv.x) + (vv.z * vv.z));
dist = max(0, dist - offset);
vv = float4(0.0f, dist * dist * -_Curvature , 0.0f, 0.0f);

EDIT: I should mention - you can't make a sphere from plane. To make believable "planet" with this method, you need to make chunks and load them while player will be close. Even then it will be just illusion. Greater angle will looks bad too.

I was searching for the same shader for the Godot Engine. Couldn't find one, but your nice sketches and comments gave me the right idea how to approach it.

Here's my solution in Godots shader language (similar to GLSL ES 3.0):

shader_type canvas_item;

uniform float radius = 2.0;

void fragment() {
    vec2 uv = SCREEN_UV;
    vec2 surface = vec2(0.5, 0.2);
    vec2 center = surface - vec2(0, radius);
    float base = length(uv - center);
    float height = base - radius;
    float xdiff = (uv.x - surface.x) / base * height;
    uv = clamp(vec2 (uv.x - xdiff, surface.y + height), vec2(0.0, 0.0), vec2(1.0, 1.0));
    COLOR.rgb = textureLod(SCREEN_TEXTURE, uv, 0.0).rgb;
}

Screenshot without shader:没有着色器的截图

Screenshot with shader:带着色器的屏幕截图

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM