The Sun, a Mass of Incandescent Gas

A giant nuclear furnace.

Uses technique similar to a few fluid simulations I found on shader toy to resemble churning plasma and create dark areas for sunspots. 

Shader code
shader_type canvas_item;
render_mode blend_premul_alpha;

// --- colors, cool -> hot ---
uniform vec3 cool_color : source_color = vec3(0.55, 0.08, 0.0);
uniform vec3 mid_color : source_color = vec3(1.0, 0.45, 0.05);
uniform vec3 hot_color : source_color = vec3(1.0, 0.92, 0.65);
uniform bool use_color_ramp = false;  // use a GradientTexture1D instead of the three colors
uniform sampler2D color_ramp : source_color, repeat_disable, filter_linear;
uniform float emission = 1.0;         // above 1 blooms if HDR 2D + glow are on
uniform float contrast = 1.5;
uniform float limb_darkening : hint_range(0.0, 1.0) = 0.6;

// --- surface ---
uniform float granulation : hint_range(0.0, 1.0) = 0.35;  // strength of the small convection cells
uniform float granule_size = 10.0;                        // higher = smaller cells

// --- sunspots ---
uniform bool spot_independent = true;                        // own slow field (persistent) vs. reusing the plasma field
uniform float spot_threshold : hint_range(0.0, 0.5) = 0.08;  // length(field) below this = spot, raise for more spots
uniform float spot_scale = 0.6;                              // multiplies the swirl scale; lower = bigger spots (independent only)
uniform float spot_drift = 0.15;                             // fraction of the flow clock; lower = longer-lived (independent only)
uniform float spot_darkness : hint_range(0.0, 1.0) = 0.85;   // how dark the umbra gets
uniform float spot_relief = 0.5;                             // ripple shading strength in the penumbra
uniform bool spot_band_only = true;                          // restrict spots to the specific latitudes
uniform vec2 spot_light_dir = vec2(-0.7, 0.7);               // relief light direction, screen space, y up

// --- corona ---
uniform float sun_radius : hint_range(0.1, 1.0) = 0.75; // disk size
uniform vec3 corona_color : source_color = vec3(1.0, 0.5, 0.1);
uniform float corona_strength = 0.8;
uniform float corona_falloff = 6.0; // higher = tighter glow

// --- custom noise ---
uniform bool use_noise_texture = false;
uniform sampler2D noise_texture : repeat_enable, filter_linear;
uniform float noise_texture_scale = 0.2;  // texture repeats per noise unit (lower = bigger features)

// --- motion ---
uniform float scale = 2.0;        // swirl size (higher = smaller swirls)
uniform float speed = 0.1;
uniform float swirl = 1.5;        // how hard the flow twists
uniform float spin_speed = 0.03;  // sphere rotation (0 = still)

const mat2 ROT = mat2(vec2(0.8, 0.6), vec2(-0.6, 0.8));

uint lowbias(uint x) {
	x ^= x >> 16u; x *= 2146121005u;
	x ^= x >> 15u; x *= 2221713035u;
	x ^= x >> 16u;
	return x;
}

vec2 hash2(vec2 p) {
	uvec2 q = uvec2(ivec2(p));
	float a = float(lowbias(q.x + lowbias(q.y)) >> 8u) * (TAU / 16777216.0);
	return vec2(cos(a), sin(a));
}

float simplex(vec2 p) {
	const float K1 = 0.366025404; // (sqrt(3)-1)/2
	const float K2 = 0.211324865; // (3-sqrt(3))/6
	vec2 i = floor(p + (p.x + p.y) * K1);
	vec2 a = p - i + (i.x + i.y) * K2;
	float m = step(a.y, a.x);
	vec2 o = vec2(m, 1.0 - m);
	vec2 b = a - o + K2;
	vec2 c = a - 1.0 + 2.0 * K2;
	vec3 h = max(0.5 - vec3(dot(a, a), dot(b, b), dot(c, c)), 0.0);
	vec3 n = h * h * h * h * vec3(dot(a, hash2(i)), dot(b, hash2(i + o)), dot(c, hash2(i + 1.0)));
	return dot(n, vec3(70.0));
}

vec4 cubic(float v) {
	vec4 n = vec4(1.0, 2.0, 3.0, 4.0) - v;
	vec4 s = n * n * n;
	float x = s.x;
	float y = s.y - 4.0 * s.x;
	float z = s.z - 4.0 * s.y + 6.0 * s.x;
	float w = 6.0 - x - y - z;
	return vec4(x, y, z, w) * (1.0 / 6.0);
}

float tex_noise(vec2 p) {
	vec2 tres = vec2(textureSize(noise_texture, 0));
	vec2 uv = p * noise_texture_scale * tres - 0.5;
	vec2 f = fract(uv);
	uv -= f;
	vec4 xc = cubic(f.x);
	vec4 yc = cubic(f.y);
	vec4 c = uv.xxyy + vec2(-0.5, 1.5).xyxy;
	vec4 s = vec4(xc.xz + xc.yw, yc.xz + yc.yw);
	vec4 o = (c + vec4(xc.yw, yc.yw) / s) / tres.xxyy;
	float sx = s.x / (s.x + s.y);
	float sy = s.z / (s.z + s.w);
	float s0 = texture(noise_texture, o.xz).r;
	float s1 = texture(noise_texture, o.yz).r;
	float s2 = texture(noise_texture, o.xw).r;
	float s3 = texture(noise_texture, o.yw).r;
	return mix(mix(s3, s2, sx), mix(s1, s0, sx), sy) * 2.0 - 1.0;
}

// use noise texture if chosen, simplex noise otherwise
float noise(vec2 p) {
	if (use_noise_texture) {
		return tex_noise(p);
	}
	return simplex(p);
}

float fbm(vec2 p) {
	float v = 0.0;
	float a = 0.5;
	for (int i = 0; i < 4; i++) {
		v += a * (0.5 + 0.5 * noise(p));
		p = ROT * p * 2.02 + vec2(3.1, 1.7);
		a *= 0.5;
	}
	return v;
}

// churning plasma flow
vec3 field(vec2 uv, float t) {
	vec2 p = uv * scale;
	vec2 q = vec2(fbm(p + vec2(0.0, t)), fbm(p + vec2(5.2, 1.3) - t));
	vec2 r = vec2(fbm(p + swirl * q + vec2(1.7, 9.2) + 0.15 * t),
				  fbm(p + swirl * q + vec2(8.3, 2.8) - 0.12 * t));
	float f = fbm(p + swirl * r);
	return vec3(r, f) - 0.5;
}

vec3 ramp(float h) {
	if (use_color_ramp) {
		return texture(color_ramp, vec2(h, 0.5)).rgb;
	}
	return mix(mix(cool_color, mid_color, smoothstep(0.0, 0.5, h)), hot_color, smoothstep(0.5, 1.0, h));
}

// screen-space point
vec2 sphere_uv(vec2 c) {
	vec2 cc = c / max(length(c), 1.0);
	vec3 N = vec3(cc, sqrt(max(1.0 - dot(cc, cc), 0.0)));
	return vec2(atan(N.x, N.z) / PI + 0.5 + TIME * spin_speed,
			0.5 - asin(clamp(N.y, -1.0, 1.0)) / PI);
}

// height map for the sunspot ripples: low = spot center
float spot_height(vec2 suv, float t) {
	if (spot_independent) {
		return length(field(suv * spot_scale, t * spot_drift));
	}
	return length(field(suv, t));
}

void fragment() {
	// screen-space derivatives first
	float aspect = abs(dFdy(UV.y) / dFdx(UV.x));   // node width / height
	vec2 fit = vec2(max(aspect, 1.0), max(1.0 / aspect, 1.0));
	vec2 c = (UV - 0.5) * 2.0 * fit * vec2(1.0, -1.0) / sun_radius;  // 1.0 = edge of the sun, y up
	float r = length(c);
	float edge = fwidth(r);
	float px = 1.4 * abs(dFdx(c.x));  // gradient step for the spot relief
	float t = TIME * speed;

	float disk = 1.0 - smoothstep(1.0 - edge, 1.0, r);  // antialiased rim

	vec3 sun = vec3(0.0);
	if (disk > 0.0) {
		// point on the front of a unit sphere
		vec2 cc = c / max(r, 1.0);
		vec3 N = vec3(cc, sqrt(max(1.0 - dot(cc, cc), 0.0)));
		float mu = N.z;  // 1 at the center, 0 at the edge
		float lon = atan(N.x, N.z);
		float lat = asin(clamp(N.y, -1.0, 1.0));
		vec2 suv = vec2(lon / PI + 0.5 + TIME * spin_speed, 0.5 - lat / PI);

		// large churning flows
		vec3 fld = field(suv, t);
		float heat = fld.z + 0.5;

		// sunspot masks
		float h = spot_independent ? spot_height(suv, t) : length(fld);
		float band = 1.0;
		if (spot_band_only) {
			float al = abs(lat);
			band = smoothstep(0.05, 0.15, al) * (1.0 - smoothstep(0.4, 0.6, al));
		}
		float penumbra = (1.0 - smoothstep(spot_threshold * 0.6, spot_threshold, h)) * band;
		float umbra = (1.0 - smoothstep(spot_threshold * 0.2, spot_threshold * 0.55, h)) * band;

		// convection cells
		float gn = noise(suv * granule_size + fld.xy * 3.0 + vec2(t * 0.7, -t * 0.5));
		float cells = smoothstep(0.0, 0.45, abs(gn));
		heat *= mix(1.0, cells, granulation * (1.0 - penumbra) * smoothstep(0.0, 0.3, mu));
		heat = clamp((heat - 0.45) * contrast + 0.55, 0.0, 1.0);
		heat *= 1.0 - 0.5 * penumbra;

		// limb darkening: dimmer/redder toward the edge
		float limb = 1.0 - limb_darkening * (1.0 - sqrt(mu));
		sun = ramp(heat * mix(1.0, limb, 0.5)) * limb * emission;

		// umbra: dark core
		sun *= 1.0 - umbra * spot_darkness;
		
		// penumbra: bump-lit ripple from the height gradient
		if (penumbra > 0.0) {
			vec2 g = vec2(spot_height(sphere_uv(c + vec2(px, 0.0)), t) - h,
					spot_height(sphere_uv(c + vec2(0.0, px)), t) - h) / px;
			float lit = dot(g, normalize(spot_light_dir) * vec2(-1.0, -1.0));
			sun *= max(1.0 + spot_relief * penumbra * mu * lit, 0.0);
		}
	}
	
	// corona
	vec2 dir = c / max(r, 1e-4);
	float flare = fbm(dir * 3.0 + (r * 1.5 - t * 4.0));
	float glow = exp(-max(r - 1.0, 0.0) * corona_falloff) * (0.4 + 1.2 * flare) * corona_strength;
	float rr = r * sun_radius;  // 1.0 = edge of the node
	glow *= 1.0 - smoothstep(sun_radius, 1.0, rr);
	vec3 corona = corona_color * glow * emission;
	
	COLOR = vec4(sun * disk + corona * (1.0 - disk), disk);
}
Live Preview
Tags
Fluid, Gas, space, star, Sun
The shader code and all code snippets in this post are under CC0 license and can be used freely without the author's permission. Images and videos, and assets depicted in those, do not fall under this license. For more info, see our License terms.

More from Axiomata

Related shaders

guest

0 Comments
Oldest
Newest Most Voted