我的三维mandelbulb制作fractal成长之路[续]

网络上一般的mandelbulb的代码会比较大,不仅仅画mandelbullb,还画其它,并且增加了许多特效,不适合入门。

目前找到 https://github.com/vpmedia/flash-pbk-collectionMandelbulbQuick.pbk,是入门ray match画mandelbulb比较方便的入门代码。这是基于adobe 的Pixel Bender Toolkit的。

基于它可以实现在mandelbulb上裹一张图片,或者根据位置高度着不同颜色。

效果实现如下:

1.外面裹图片。

2. 根据位置高度着不同颜色

那么基于这个起点,就可以随意做自己的三维分形图了。

修改颜色搭配,power>8的情况。

修改后的pbk代码:

/**
 * MandelbulbQuick.pbk
 * Last update: 14 December 2009
 *
 * Changelog:
 *		1.0		- Initial release
 *		1.0.1	- Fixed a missing asymmetry thanks to Chris King (http://www.dhushara.com)
 *				- Refinements in the colouring
 *      1.0.2   - Added radiolaria option for a funky hair-like effect
 * 				- Incorporated the scalar derivative method as described here:
 *			    - http://www.fractalforums.com/mandelbulb-implementation/realtime-renderingoptimisations/
 *		1.0.3	- Created a quick version of the script as using a boolean flag to determine
 *				  which distance estimation method created long compilation times.
 * 		1.0.4 	- Fixed issue with older graphic cards and the specular highlights
 *
 * 
 * Copyright (c) 2009 Tom Beddard
 * http://www.subblue.com
 *
 * For more Flash and PixelBender based generative graphics experiments see:
 * http://www.subblue.com/blog
 *
 * Licensed under the MIT License:
 * http://www.opensource.org/licenses/mit-license.php
 *
 *
 * Credits and references
 * ======================
 * For the story behind the 3D Mandelbrot see the following page:
 * http://www.skytopia.com/project/fractal/mandelbulb.html
 *
 * The original forum disussion with many implementation details can be found here:
 * http://www.fractalforums.com/3d-fractal-generation/true-3d-mandlebrot-type-fractal/
 *
 * This implementation references the 4D Quaternion GPU Raytracer by Keenan Crane:
 * http://www.devmaster.net/forums/showthread.php?t=4448
 *
 * and the NVIDIA CUDA/OptiX implementation by cbuchner1:
 * http://forums.nvidia.com/index.php?showtopic=150985
 *
 */

#define PI 3.141592653
#define MIN_EPSILON 3e-7

<languageVersion : 1.0;>

kernel MandelbulbQuick
<	namespace : "com.subblue.filters";
	vendor : "Tom Beddard";
	version : 1;
	description : "Mandelbulb Fractal Ray Tracer - the quick version";
>


{
	parameter int antialiasing
	<
		minValue:1;
		maxValue:3;
		defaultValue:1;
		description:"Super sampling quality. Number of samples squared per pixel.";
	>;

	parameter bool phong
	<
		defaultValue:true;
		description: "Enable phong shading.";
	>;

	parameter bool julia
	<
		defaultValue:false;
		description: "Enable Julia set version.";
	>;

	parameter bool radiolaria
	<
		defaultValue:false;
		description: "Enable radiolaria style.";
	>;

	parameter float radiolariaFactor
	<
		minValue: -4.0;
		maxValue: 4.0;
		defaultValue: 0.0;
		description: "Tweak the radiolaria effect.";
	>;

	parameter float shadows
	<
		minValue:0.0;
		maxValue:1.0;
		defaultValue:0.0;
		description: "Enable ray traced shadows.";
	>;

	parameter float ambientOcclusion
	<
		minValue:0.0;
		maxValue:1.0;
		defaultValue:0.8;
		description: "Enable fake ambient occlusion factor based on the orbit trap.";
	>;

	parameter float ambientOcclusionEmphasis
	<
		minValue:0.0;
		maxValue:1.0;
		defaultValue:0.58;
		description: "Emphasise the structure edges based on the number of steps it takes to reach a point in the fractal.";
	>;

	parameter float bounding
	<
		minValue:1.0;
		maxValue:16.0;
		defaultValue:2.5;
		description: "Sets the bounding sphere radius to help accelerate the raytracing.";
	>;

	parameter float bailout
	<
		minValue:0.5;
		maxValue:12.0;
		defaultValue:4.0;
		description: "Sets the bailout value for the fractal calculation. Lower values give smoother less detailed results.";
	>;

	parameter float power
	<
		minValue:2.0;
		maxValue:32.0;
		defaultValue:8.0;
		description: "The power of the fractal.";
	>;
	parameter float textureRate
	<
		minValue:0.0;
		maxValue:1.0;
		defaultValue:0.210;
		description: "rate of texture.";
	>; 
	parameter float textureStartc
	<
		minValue:0.0;
		maxValue:380.0;
		defaultValue:10.50;
		description: "start H of texture.";
	>;      
	parameter float2 textureSize
	<
		minValue: float2(50.0, 50.0);
		maxValue:float2(2500.0, 1500.0);
		defaultValue:float2(500.0, 250.0);
		description: "Tweak the mapping of the triplex numbers into spherical co-ordinates - in other words tweak the surface shape.";
	>;
	parameter float3 julia_c
	<
		minValue:float3(-2, -2, -2);
		maxValue:float3(2, 2, 2);
		defaultValue:float3(1.0, 0.0, 0.0);
		description: "The c constant for Julia set fractals";
	>;

	parameter float3 cameraPosition
	<
		minValue:float3(-4, -4, -4);
		maxValue:float3(4, 4, 4);
		defaultValue:float3(0, -2.6, 0);
		description: "Camera position.";
	>;

	parameter float3 cameraPositionFine
	<
		minValue:float3(-0.1, -0.1, -0.1);
		maxValue:float3(0.1, 0.1, 0.1);
		defaultValue:float3(0, 0.0, 0.0);
		description: "Fine tune position.";
	>;

	parameter float3 cameraRotation
	<
		minValue:float3(-180, -180, -180);
		maxValue:float3(180, 180, 180);
		defaultValue:float3(0, 0, -90);
		description: "Pointing angle in each axis of the camera.";
	>;

	parameter float cameraZoom
	<
		minValue:0.0;
		maxValue:10.0;
		defaultValue:0.0;
		description: "Zoom the camera view.";
	>;

	parameter float3 light
	<
		minValue:float3(-50, -50, -50);
		maxValue:float3(50, 50, 50);
		defaultValue:float3(38, -42, 38);
		description: "Position of point light.";
	>;

	parameter float3 colorBackground
	<
		minValue:float3(0, 0, 0);
		maxValue:float3(1, 1, 1);
		defaultValue:float3(0.0, 0.0, 0.0);
		description: "Background colour.";
        aeUIControl: "aeColor";
	>;
    
    parameter float colorBackgroundTransparency
	<
		minValue:float(0.0);
		maxValue:float(1.0);
		defaultValue:float(1.0);
		description: "Background transparency.";
	>;

	parameter float3 colorDiffuse
	<
		minValue:float3(0, 0, 0);
		maxValue:float3(1, 1, 1);
		defaultValue:float3(0.0, 0.85, 0.99);
		description: "Diffuse colour.";
        aeUIControl: "aeColor";
	>;

	parameter float3 colorAmbient
	<
		minValue:float3(0, 0, 0);
		maxValue:float3(1, 1, 1);
		defaultValue:float3(0.67, 0.85, 1.0);
		description: "Ambient light colour.";
        aeUIControl: "aeColor";
	>;
	
	parameter float colorAmbientIntensity
	<
		minValue:float(0);
		maxValue:float(1);
		defaultValue:float(0.4);
		description: "Ambient light intensity.";
	>;

	parameter float3 colorLight
	<
		minValue:float3(0, 0, 0);
		maxValue:float3(1, 1, 1);
		defaultValue:float3(0.48, 0.59, 0.66);
		description: "Light colour.";
        aeUIControl: "aeColor";
	>;

	parameter float colorSpread
	<
		minValue:0.0;
		maxValue:1.0;
		defaultValue:0.2;
		description: "Vary the colour based on the normal direction.";
	>;

	parameter float rimLight
	<
		minValue:0.0;
		maxValue:1.0;
		defaultValue:0.0;
		description: "Rim light factor.";
	>;

	parameter float specularity
	<
		minValue:0.0;
		maxValue:1.0;
		defaultValue:0.66;
		description: "Phone specularity";
	>;

	parameter float specularExponent
	<
		minValue:0.1;
		maxValue:50.0;
		defaultValue:15.0;
		description: "Phong shininess";
	>;

	parameter float3 rotation
	<
		minValue:float3(-180, -180, -180);
		maxValue:float3(180, 180, 180);
		defaultValue:float3(0, 36, 39.6);
		description: "Rotate the Mandelbulb in each axis.";
	>;

	parameter int maxIterations
	<
		minValue:1;
		maxValue:20;
		defaultValue:6;
		description: "More iterations reveal more detail in the fractal surface but takes longer to calculate.";
	>;

	parameter int stepLimit
	<
		minValue:10;
		maxValue:200;
		defaultValue:110;
		description: "The maximum number of steps a ray should take.";
	>;

	parameter float epsilonScale
	<
		minValue:0.1;
		maxValue:1.0;
		defaultValue:1.0;
		description: "Scale the epsilon step distance. Smaller values are slower but will generate smoother results for thin areas.";
	>;

	parameter int2 size
	<
		minValue:int2(100, 100);
		maxValue:int2(2048, 2048);
		defaultValue:int2(640, 480);
		description: "The output size in pixels.";
	>;

	region generated()
	{
		return region(float4(0, 0, size.x, size.y));
	}

	dependent float bailout_sr, aspectRatio, sampleStep, sampleContribution, pixel_scale, eps_start;
	dependent float3 eye;
	dependent float3x3 viewRotation, objRotation;
	input image4 src;
	output pixel4 dst;


	// Scalar derivative approach by Enforcer:
	// http://www.fractalforums.com/mandelbulb-implementation/realtime-renderingoptimisations/
	void powN(inout float3 z, float zr0, inout float dr)
	{
		float zo0 = asin(z.z / zr0);
		float zi0 = atan(z.y, z.x);
		float zr = pow(zr0, power - 1.0);
		float zo = (zo0) * power;
		float zi = (zi0) * power;
		float czo = cos(zo);

		dr = zr * dr * power + 1.0;
		zr *= zr0;

		z = zr * float3(czo*cos(zi), czo*sin(zi), sin(zo));
	}


	// The fractal calculation
	//
	// Calculate the closest distance to the fractal boundary and use this
	// distance as the size of the step to take in the ray marching.
	//
	// Fractal formula:
	//	  z' = z^p + c
	//
	// For each iteration we also calculate the derivative so we can estimate
	// the distance to the nearest point in the fractal set, which then sets the
	// maxiumum step we can move the ray forward before having to repeat the calculation.
	//
	//	 dz' = p * z^(p-1)
	//
	// The distance estimation is then calculated with:
	//
	//   0.5 * |z| * log(|z|) / |dz|
	//
	float DE(float3 z0, inout float min_dist)
	{
		float3 c = julia ? julia_c : z0; // Julia set has fixed c, Mandelbrot c changes with location
		float3 z = z0;

		float dr = 1.0;
		float r	 = length(z);
		if (r < min_dist) min_dist = r;

		for (int n = 0; n < maxIterations; n++) {
			powN(z, r, dr);
			z += c;

			if (radiolaria && z.y > radiolariaFactor) z.y = radiolariaFactor;

			r = length(z);
			if (r < min_dist) min_dist = r;
			if (r > bailout) break;
		}

		return 0.5 * log(r) * r / dr;
	}
	

	// Intersect bounding sphere
	//
	// If we intersect then set the tmin and tmax values to set the start and
	// end distances the ray should traverse.
	bool intersectBoundingSphere(float3 origin,
								 float3 direction,
								 out float tmin,
								 out float tmax)
	{
		bool hit = false;

		float b = dot(origin, direction);
		float c = dot(origin, origin) - bounding;
		float disc = b*b - c;			// discriminant
		tmin = tmax = 0.0;

		if (disc > 0.0) {
			// Real root of disc, so intersection
			float sdisc = sqrt(disc);
			float t0 = -b - sdisc;			// closest intersection distance
			float t1 = -b + sdisc;			// furthest intersection distance

			if (t0 >= 0.0) {
				// Ray intersects front of sphere
				float min_dist;
				float3 z = origin + t0 * direction;
				tmin = DE(z, min_dist);
				tmax = t0 + t1;
			} else if (t0 < 0.0) {
				// Ray starts inside sphere
				float min_dist;
				float3 z = origin;
				tmin = DE(z, min_dist);
				tmax = t1;
			}
			hit = true;
		}

		return hit;
	}


	// Calculate the gradient in each dimension from the intersection point
	float3 estimate_normal(float3 z, float e)
	{
		float min_dst;	// Not actually used in this particular case
		float3 z1 = z + float3(e, 0, 0);
		float3 z2 = z - float3(e, 0, 0);
		float3 z3 = z + float3(0, e, 0);
		float3 z4 = z - float3(0, e, 0);
		float3 z5 = z + float3(0, 0, e);
		float3 z6 = z - float3(0, 0, e);

		float dx = DE(z1, min_dst) - DE(z2, min_dst);
		float dy = DE(z3, min_dst) - DE(z4, min_dst);
		float dz = DE(z5, min_dst) - DE(z6, min_dst);

		return normalize(float3(dx, dy, dz) / (2.0*e));
	}


	// Computes the direct illumination for point pt with normal N due to
	// a point light at light and a viewer at eye.
	float3 Phong(float3 pt, float3 N, out float specular)
	{
		float3 diffuse	= float3(0, 0, 0);			// Diffuse contribution
		float3 color	= float3(0, 0, 0);
        specular = 0.0;

		float3 L = normalize(light * objRotation - pt); // find the vector to the light
		float  NdotL = dot(N, L);			// find the cosine of the angle between light and normal

		if (NdotL > 0.0) {
			// Diffuse shading
			diffuse = colorDiffuse + abs(N) * colorSpread;
			diffuse *= colorLight * NdotL;

			// Phong highlight
			float3 E = normalize(eye - pt);		// find the vector to the eye
			float3 R = L - 2.0 * NdotL * N;		// find the reflected vector
			float  RdE = dot(R,E);

			if (RdE <= 0.0) {
				specular = specularity * pow(abs(RdE), specularExponent);
            }
		} else {
			diffuse = colorDiffuse * abs(NdotL) * rimLight;
		}

		return (colorAmbient * colorAmbientIntensity) + diffuse;
	}

	// Calculate the gradient in each dimension from the intersection point
	float2 estimate_texture(float3 zi)
	{
        float2 uv=float2(0,0);
        float3 z=normalize(zi);
		float min_dst;	// Not actually used in this particular case
		float3 z1 =float3(0, 0, 1.0);
		float3 y1 =float3(1.0, 0.0, 0);
        float phi = acos( dot( z, z1 ));
        uv.y = phi / PI;
        float u=0.0;
        if(abs(phi)<0.05)phi=0.05;
        float theta = ( acos( dot( z, y1 ) / sin( phi )) ) / ( 2.0 * PI) ;
        if( dot( cross( z1, y1 ), z )>0.0 )
           u = theta;
        else
           u = 1.0 - theta;
        uv.x=u;
		return uv;
	}
	// Define the ray direction from the pixel coordinates
	float3 rayDirection(float2 p)
	{
		float3 direction = float3( 2.0 * aspectRatio * p.x / float(size.x) - aspectRatio,
								  -2.0 * p.y / float(size.y) + 1.0,
								  -2.0 * exp(cameraZoom));
		return normalize(direction * viewRotation * objRotation);
	}
    //h=(0.0,360.0) s=(0.0,1.0) v=(0.0,1.0)
    float4 HSVToRGB(float h,float s,float v)
    {
        float4 color=float4(0,0,0,1);
        float angle;
        const float f25=255.0;
        int i;
        int clra,clrb,clrc;
        
        h=mod(h,360.0)/60.0;
        i=int(h);
        angle=h-float(i);
        
        clra=int(v*(1.0 - s)*f25);
        clrb=int(v*(1.0 - s*angle)*f25);
        clrc=int(v*(1.0 - s*(1.0-angle))*f25);
        
          if(i==0){
            color.rgb=float3(v*f25,clrc,clra);
            }else if(i==1){
            color.rgb=float3(clrb,v*f25,clra);
            }  else if(i==2){
            color.rgb=float3(clra,v*f25,clrc);
            }  else if(i==3){
            color.rgb=float3(clra,clrb,v*f25);
            }   else if(i==4){
            color.rgb=float3(clrc,clra,v*f25);
            } else {
            color.rgb=float3(v*f25,clra,clrb);
            }

           return color;
    }
    void HSVtoRGB1(out float  r, out float   g, out float  b, float h, float s, float v )
    {
        int i;
        float f25=1.0;//255.0;
        float f, p, q, t;
        h=mod(h,360.0);
        if( s == 0.0 ) {
            // achromatic (grey)
             r =  g =  b = v;
           
        } else {
        h /= 60.0;            // sector 0 to 5
        i = int( h );
        f = h - float(i);          // factorial part of h
        p = v * ( 1.0 - s );
        q = v * ( 1.0 - s * f );
        t = v * ( 1.0  - s * ( 1.0 - f ) );
        if(i==0){
                 r = v;
                 g = t;
                 b = p;
        }else if(i==1){
     
                 r = q;
                 g = v;
                 b = p;
        }else if(i==2){
                 r = p;
                 g = v;
                 b = t;
        }else if(i==3){
                 r = p;
                 g = q;
                 b = v;
        }else if(i==4){
                 r = t;
                 g = p;
                 b = v;
            }else {
                 r = v;
                 g = p;
                 b = q;
        }
        }
        r*=f25;
        g*=f25;
        b*=f25;
     
    }
    float4  HSVtoRGB3(float h, float s, float v) 
    {
       float4 c =float4(0,0,0,1.0);
       HSVtoRGB1(c.r,c.g,c.b,h,s,v);
       return c;
    }
	// Calculate the output colour for each input pixel
	float4 renderPixel(float2 pixel,inout float4  textureColor)
	{
		float tmin, tmax;
		float3 ray_direction = rayDirection(pixel);
		float4 color;
        color.rgb = colorBackground.rgb;
        color.a = colorBackgroundTransparency;

		if (intersectBoundingSphere(eye, ray_direction, tmin, tmax)) {
			float3 ray = eye + tmin * ray_direction;

			float dist, ao;
			float min_dist = 4.0;
			float ray_length = tmin;
			float eps = MIN_EPSILON;

			// number of raymarching steps scales inversely with factor
			int max_steps = int(float(stepLimit) / epsilonScale);
			int i;
			float f;

			for (i = 0; i < max_steps; ++i) {
				dist = DE(ray, min_dist);

				// March ray forward
				f = epsilonScale * dist;
				ray += f * ray_direction;
				ray_length += f * dist;

				// Are we within the intersection threshold or completely missed the fractal
				if (dist < eps || ray_length > tmax) break;

				// Set the intersection threshold as a function of the ray length from the camera
				eps = max(MIN_EPSILON, pixel_scale * ray_length);
			}


			// Found intersection?
			if (dist < eps) {
				ao	= 1.0 - clamp(1.0 - min_dist * min_dist, 0.0, 1.0) * ambientOcclusion;

				if (phong) {
					float3 normal = estimate_normal(ray, eps/2.0);
					float specular = 0.0;
                    float2 uvdst=float2(0,0);
                    float2 uv=estimate_texture(ray);
                    float2 srcsize=textureSize;//pixelSize(src);
                    uvdst.x=uv.x*srcsize.x;
                    uvdst.y=uv.y*srcsize.y;
                    //uvdst=mod(uvdst,srcsize);
                    //textureColor=sampleNearest(src, uvdst); 

                                 
					color.rgb = Phong(ray, normal, specular).rgb;

					if (shadows > 0.0) {
						// The shadow ray will start at the intersection point and go
						// towards the point light. We initially move the ray origin
						// a little bit along this direction so that we don't mistakenly
						// find an intersection with the same point again.
						float3 light_direction = normalize((light - ray) * objRotation);
						ray += normal * eps * 2.0;

						float min_dist2;
						dist = 4.0;

						for (int j = 0; j < max_steps; ++j) {
							dist = DE(ray, min_dist2);

							// March ray forward
							f = epsilonScale * dist;
							ray += f * light_direction;

							// Are we within the intersection threshold or completely missed the fractal
							if (dist < eps || dot(ray, ray) > bounding * bounding) break;
						}

						// Again, if our estimate of the distance to the set is small, we say
						// that there was a hit and so the source point must be in shadow.
						if (dist < eps) {
							color.rgb *= 1.0 - shadows;
						} else {
							// Only add specular component when there is no shadow
							color.rgb += specular;
						}
					} else {
						color.rgb += specular;
					}

				} else {
					// Just use the base colour
					color.rgb = colorDiffuse;
				}
                float rr=length(ray);
                //textureColor.rgb=HSVtoRGB3(textureStartc+ mod(rr*100.0,30.0)*8.0,0.5,0.5).rgb;    //float3(40,0,140)*0.5; // 
                textureColor.rgb=HSVtoRGB3(textureStartc+ rr*150.0,0.9,0.9).rgb;   
                //textureColor.rgb=HSVtoRGB3(textureStartc+ rr*150.0,rr,0.9).rgb;                 
				//ao *= 1.0 - (float(i) / float(max_steps)) * ambientOcclusionEmphasis * 2.0;
				color.rgb *= ao;
                color.a = 1.0;
			}
		}

		return clamp(color, 0.0, 1.0);
	}


	// Common values used by all pixels
	void evaluateDependents()
	{
		aspectRatio = float(size.x) / float(size.y);

		// Camera orientation
		float c1 = cos(radians(-cameraRotation.x));
		float s1 = sin(radians(-cameraRotation.x));
		float3x3 viewRotationY = float3x3( c1, 0, s1,
											0, 1, 0,
										  -s1, 0, c1);

		float c2 = cos(radians(-cameraRotation.y));
		float s2 = sin(radians(-cameraRotation.y));
		float3x3 viewRotationZ = float3x3( c2, -s2, 0,
										   s2, c2, 0,
											0, 0, 1);

		float c3 = cos(radians(-cameraRotation.z));
		float s3 = sin(radians(-cameraRotation.z));
		float3x3 viewRotationX = float3x3( 1, 0, 0,
										   0, c3, -s3,
										   0, s3, c3);

		viewRotation = viewRotationX * viewRotationY * viewRotationZ;

		// Object rotation
		c1 = cos(radians(-rotation.x));
		s1 = sin(radians(-rotation.x));
		float3x3 objRotationY = float3x3( c1, 0, s1,
											0, 1, 0,
										  -s1, 0, c1);

		c2 = cos(radians(-rotation.y));
		s2 = sin(radians(-rotation.y));
		float3x3 objRotationZ = float3x3( c2, -s2, 0,
										   s2, c2, 0,
											0, 0, 1);

		c3 = cos(radians(-rotation.z));
		s3 = sin(radians(-rotation.z));
		float3x3 objRotationX = float3x3( 1, 0, 0,
										   0, c3, -s3,
										   0, s3, c3);

		objRotation = objRotationX * objRotationY * objRotationZ;
		
		eye = (cameraPosition + cameraPositionFine);
		if (eye == float3(0, 0, 0)) eye = float3(0, 0.0001, 0);

		eye *= objRotation;

		// Super sampling
		sampleStep = 1.0 / float(antialiasing);
		sampleContribution = 1.0 / pow(float(antialiasing), 2.0);
		pixel_scale = 1.0 / max(float(size.x), float(size.y));
	}


	// The main loop
	void evaluatePixel()
	{
		float4 color = float4(0, 0, 0, 0);
        float frate=0.001;
        float4  textureColor= float4(0, 0, 0, 0);
		if (antialiasing > 1) {
			// Average antialiasing^2 points per pixel
			for (float i = 0.0; i < 1.0; i += sampleStep)
				for (float j = 0.0; j < 1.0; j += sampleStep)
					color += sampleContribution * renderPixel(float2(outCoord().x + i, outCoord().y + j),textureColor);
		} else {
			color = renderPixel(outCoord(),textureColor);
		}

		// Return the final color which is still the background color if we didn't hit anything.
        if(dot(color.rgb,color.rgb)>0.0)
        { 
          frate=textureRate; //*0.01;
          color.rgb=color.rgb*(1.0-frate)+textureColor.rgb*frate;
        }
		dst = color;

	}
}

分形背后的数学公式推导:见下面整理文档。

https://download.csdn.net/download/foolpanda1168/10913752

发布了29 篇原创文章 · 获赞 5 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/foolpanda1168/article/details/86101091
今日推荐