Render roads directly as part of the tile texture in shaders/simple.frag
This commit is contained in:
+108
-12
@@ -17,6 +17,16 @@ uniform int isFarm;
|
||||
uniform float farmTextureScale;
|
||||
uniform int farmMappingMode;
|
||||
|
||||
uniform int hasRoad;
|
||||
uniform int roadMask;
|
||||
uniform int roadType;
|
||||
uniform float roadRadius;
|
||||
uniform float roadBorderWidth;
|
||||
uniform float roadTextureScale;
|
||||
uniform float roadOpacity;
|
||||
uniform vec3 roadColor;
|
||||
uniform vec3 roadBorderColor;
|
||||
|
||||
uniform float time;
|
||||
uniform int riverMask;
|
||||
|
||||
@@ -79,6 +89,19 @@ const vec2 EDGE_MIDPOINTS[6] = vec2[6](
|
||||
vec2(-0.375, -0.21650635)
|
||||
);
|
||||
|
||||
float bezierCurveDist(vec2 p, vec2 p0, vec2 p2) {
|
||||
vec2 s0 = p0;
|
||||
vec2 s1 = 0.5625 * p0 + 0.0625 * p2;
|
||||
vec2 s2 = 0.2500 * p0 + 0.2500 * p2;
|
||||
vec2 s3 = 0.0625 * p0 + 0.5625 * p2;
|
||||
vec2 s4 = p2;
|
||||
float d0 = distToSegment(p, s0, s1);
|
||||
float d1 = distToSegment(p, s1, s2);
|
||||
float d2 = distToSegment(p, s2, s3);
|
||||
float d3 = distToSegment(p, s3, s4);
|
||||
return min(min(d0, d1), min(d2, d3));
|
||||
}
|
||||
|
||||
float getRiverDistance(vec2 p, int mask) {
|
||||
if (mask == 0) return 999.0;
|
||||
|
||||
@@ -92,18 +115,7 @@ float getRiverDistance(vec2 p, int mask) {
|
||||
}
|
||||
|
||||
if (count == 2) {
|
||||
vec2 p0 = EDGE_MIDPOINTS[edges[0]];
|
||||
vec2 p2 = EDGE_MIDPOINTS[edges[1]];
|
||||
vec2 s0 = p0;
|
||||
vec2 s1 = 0.5625 * p0 + 0.0625 * p2;
|
||||
vec2 s2 = 0.2500 * p0 + 0.2500 * p2;
|
||||
vec2 s3 = 0.0625 * p0 + 0.5625 * p2;
|
||||
vec2 s4 = p2;
|
||||
float d0 = distToSegment(p, s0, s1);
|
||||
float d1 = distToSegment(p, s1, s2);
|
||||
float d2 = distToSegment(p, s2, s3);
|
||||
float d3 = distToSegment(p, s3, s4);
|
||||
return min(min(d0, d1), min(d2, d3));
|
||||
return bezierCurveDist(p, EDGE_MIDPOINTS[edges[0]], EDGE_MIDPOINTS[edges[1]]);
|
||||
} else {
|
||||
float minDist = 999.0;
|
||||
for (int i = 0; i < count; ++i) {
|
||||
@@ -114,6 +126,35 @@ float getRiverDistance(vec2 p, int mask) {
|
||||
}
|
||||
}
|
||||
|
||||
float getRoadDistance(vec2 p, int mask) {
|
||||
int edges[6];
|
||||
int count = 0;
|
||||
for (int e = 0; e < 6; ++e) {
|
||||
if ((mask & (1 << e)) != 0) {
|
||||
edges[count] = e;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
if (count == 0) {
|
||||
// Unconnected road on a single tile: short natural country track segment at tile center
|
||||
return distToSegment(p, vec2(-0.14, -0.05), vec2(0.14, 0.05));
|
||||
} else if (count == 1) {
|
||||
return distToSegment(p, EDGE_MIDPOINTS[edges[0]], vec2(0.0));
|
||||
} else if (count == 2) {
|
||||
return bezierCurveDist(p, EDGE_MIDPOINTS[edges[0]], EDGE_MIDPOINTS[edges[1]]);
|
||||
} else {
|
||||
float minDist = 999.0;
|
||||
for (int i = 0; i < count; ++i) {
|
||||
minDist = min(minDist, distToSegment(p, EDGE_MIDPOINTS[edges[i]], vec2(0.0)));
|
||||
for (int j = i + 1; j < count; ++j) {
|
||||
minDist = min(minDist, bezierCurveDist(p, EDGE_MIDPOINTS[edges[i]], EDGE_MIDPOINTS[edges[j]]));
|
||||
}
|
||||
}
|
||||
return minDist;
|
||||
}
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec3 norm = normalize(vNormal);
|
||||
|
||||
@@ -276,6 +317,61 @@ void main() {
|
||||
vec4 nonSnowLand = mix(biomeGround, cliffRockColor, rockWeight);
|
||||
vec4 landColor = mix(nonSnowLand, snowColor, snowWeight);
|
||||
|
||||
// 6b. Road Texture Integration (blended directly into the tile surface texture)
|
||||
if (hasRoad == 1 && oceanWaterFac < 0.95) {
|
||||
float rawRoadDist = getRoadDistance(fragVert.xy, roadMask);
|
||||
float rScale = max(0.5, roadTextureScale);
|
||||
vec2 uvRoad = vWorldPos.xy * rScale;
|
||||
|
||||
vec3 soilTex = mix(texture(texSand, uvRoad).rgb, texture(texTundra, uvRoad * 1.45).rgb, 0.45);
|
||||
vec3 stoneTex = texture(texRock, uvRoad * 1.35).rgb;
|
||||
|
||||
// Subtle organic edge roughness from soil texture grain so road borders blend naturally into terrain
|
||||
float edgeGrain = (soilTex.r - 0.5) * 0.008;
|
||||
float dRoad = max(0.0, rawRoadDist + edgeGrain);
|
||||
|
||||
float rCore = max(0.014, roadRadius);
|
||||
float rOuter = rCore + max(0.006, roadBorderWidth);
|
||||
|
||||
float roadCoreFac = 1.0 - smoothstep(rCore * 0.50, rCore, dRoad);
|
||||
float roadTotalFac = 1.0 - smoothstep(rCore * 0.65, rOuter, dRoad);
|
||||
|
||||
if (roadTotalFac > 0.001) {
|
||||
vec3 roadSurf;
|
||||
if (roadType == 1) {
|
||||
// Stone / Paved road: rich stone texture modulated by configured colors
|
||||
vec3 stoneCenter = mix(stoneTex, roadColor, 0.55) * (0.75 + 0.50 * stoneTex.g);
|
||||
vec3 stoneEdge = mix(stoneTex, roadBorderColor, 0.65) * 0.85;
|
||||
roadSurf = mix(stoneEdge, stoneCenter, roadCoreFac);
|
||||
} else if (roadType == 2) {
|
||||
// Railroad: gravel ballast bed with dark twin rail tracks
|
||||
vec3 ballast = mix(stoneTex, roadBorderColor, 0.60);
|
||||
float railLine = smoothstep(rCore * 0.35, rCore * 0.50, rawRoadDist)
|
||||
* (1.0 - smoothstep(rCore * 0.55, rCore * 0.72, rawRoadDist));
|
||||
roadSurf = mix(ballast, roadColor * (0.8 + 0.4 * stoneTex.r), max(roadCoreFac * 0.6, railLine));
|
||||
} else {
|
||||
// Dirt road: natural packed earth & wheel ruts blended with soil texture
|
||||
float rutPattern = smoothstep(rCore * 0.25, rCore * 0.65, rawRoadDist);
|
||||
vec3 packedEarth = mix(soilTex, roadColor, 0.52) * (0.78 + 0.45 * soilTex.r);
|
||||
vec3 wornShoulder = mix(soilTex, roadBorderColor, 0.60) * (0.72 + 0.40 * soilTex.g);
|
||||
vec3 innerTrack = mix(packedEarth, wornShoulder, rutPattern * 0.45);
|
||||
roadSurf = mix(wornShoulder, innerTrack, roadCoreFac);
|
||||
}
|
||||
|
||||
// If crossing a river, render a wooden/stone bridge deck and suppress river water under the bridge
|
||||
if (riverWaterFac > 0.01) {
|
||||
vec3 bridgeCol = (roadType >= 1)
|
||||
? mix(stoneTex, vec3(0.58, 0.56, 0.53), 0.65)
|
||||
: mix(soilTex, vec3(0.55, 0.38, 0.20), 0.70);
|
||||
roadSurf = mix(roadSurf, bridgeCol, riverWaterFac);
|
||||
waterFac *= (1.0 - roadTotalFac * 0.95);
|
||||
}
|
||||
|
||||
float blendAlpha = roadTotalFac * clamp(roadOpacity, 0.0, 1.0) * (1.0 - oceanWaterFac);
|
||||
landColor.rgb = mix(landColor.rgb, roadSurf, blendAlpha);
|
||||
}
|
||||
}
|
||||
|
||||
// 7. Combine Land and Sea
|
||||
vec4 surfaceColor = mix(landColor, waterColor, waterFac);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user