Skip to content

.NET API

jesterKing edited this page Oct 22, 2014 · 3 revisions

Using the .NET API

CSycles is the .NET API to Cycles. The following shows a minimal example of a program to create a rendered image with Cycles.

example

using System;
using System.Drawing;
using ccl;
using ccl.ShaderNodes;

namespace SmallCSycles
{

	class Program
	{
		#region meshdata
		static float[] vert_floats =
		{
			1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f
		};
		readonly static int[] nverts =
		{
			4, 4, 4, 4, 4, 4
		};
		readonly static int[] vertex_indices =
		{
			0, 1, 2, 3, 4, 7, 6, 5, 0, 4, 5, 1, 1, 5, 6, 2, 2, 6, 7, 3, 4, 0, 3, 7
		};
		#endregion
		static Session Session { get; set; }
		static Client Client { get; set; }
		private const uint width = 800;
		private const uint height = 600;
		private const uint samples = 10;

		private static CSycles.UpdateCallback g_update_callback;
		private static CSycles.RenderTileCallback g_update_render_tile_callback;
		private static CSycles.RenderTileCallback g_write_render_tile_callback;

		/// <summary>
		/// Clamp values to 0-255 range
		/// </summary>
		/// <param name="ch"></param>
		/// <returns></returns>
		public static int ColorClamp(int ch)
		{
			if (ch < 0) return 0;
			return ch > 255 ? 255 : ch;
		}

		/// <summary>
		/// Callback used to retrieve render progress information from Cycles.
		/// </summary>
		/// <param name="sessionId"></param>
		static public void StatusUpdateCallback(uint sessionId)
		{
			float progress;
			double total_time;

			CSycles.progress_get_progress(Client.Id, sessionId, out progress, out total_time);
			var status = CSycles.progress_get_status(Client.Id, sessionId);
			var substatus = CSycles.progress_get_substatus(Client.Id, sessionId);
			uint samples;
			uint num_samples;

			CSycles.tilemanager_get_sample_info(Client.Id, sessionId, out samples, out num_samples);

			if (status.Equals("Finished"))
			{
				Console.WriteLine("wohoo... :D");
			}

			status = "[" + status + "]";
			if (!substatus.Equals(string.Empty)) status = status + ": " + substatus;
			Console.WriteLine("C# status update: {0} {1} {2} {3} <|> {4:N}s {5:P}", CSycles.progress_get_sample(Client.Id, sessionId), status, samples, num_samples, total_time, progress);
		}

		static public void SimpleRenderTileCallback(uint sessionId, uint x, uint y, uint w, uint h, uint depth)
		{
			// do nothing
		}


		static void Main(string[] args)
		{
			/* first set path to CUDA pre-compiled binaries, then initialise. */
			CSycles.set_kernel_path("lib");
			CSycles.initialise();

			/* create a new client. */
			Client = new Client();

			/* Get a rendering device. FirstCuda will give the first CUDA device
			 * found, or failing that the default device: CPU.
			 */
			var dev = Device.FirstCuda;
			Console.WriteLine("Using {0} {1}", dev.Name, dev.Description);

			/* set up scene parameters. SVM is currently only supported shading system in CSycles. */
			var scene_params = new SceneParameters(Client, ShadingSystem.SVM, BvhType.Static, false, false, false, false);

			/* create a scene. */
			var scene = new Scene(Client, scene_params, dev);

			/* move the scene camera a bit, so we can see our render result. */
			var t = Transform.Identity();
			t = t*Transform.Rotate(75.0f * (float)Math.PI / 180.0f, new float4(0.0f, 1.0f, 1.0f));
			t = t*Transform.Translate(0.0f, 0.0f, -10f);
			scene.Camera.Matrix = t;
			/* set also the camera size = render resolution in pixels. Also do some extra settings. */
			scene.Camera.Size = new Size((int)width, (int)height);
			scene.Camera.Type = CameraType.Perspective;
			scene.Camera.ApertureSize = 0.0f;
			scene.Camera.Fov = 0.661f;
			scene.Camera.FocalDistance = 0.0f;
			scene.Camera.SensorWidth = 32.0f;
			scene.Camera.SensorHeight = 18.0f;

			/* Create a simple surface shader and make it the default surface shader */
			var surface = new Shader(Client, Shader.ShaderType.Material)
			{
				Name = "SmallCSycles material",
				UseMis = false,
				UseTransparentShadow = true,
				HeterogeneousVolume = false
			};

			var diffbsdf = new DiffuseBsdfNode();
			diffbsdf.ins.Color.Value = new float4(0.8f, 0.4f, 0.2f);
			diffbsdf.ins.Roughness.Value = 0.0f;
			surface.AddNode(diffbsdf);

			diffbsdf.outs.BSDF.Connect(surface.Output.ins.Surface);
			surface.FinalizeGraph();

			scene.AddShader(surface);
			scene.DefaultSurface = surface;

			/* simple background shader */
			var background = new Shader(Client, Shader.ShaderType.World)
			{
				Name = "SmallCSycles world",
				UseMis = false,
				UseTransparentShadow = true,
				HeterogeneousVolume = false
			};

			var bgnode = new BackgroundNode();
			bgnode.ins.Color.Value = new float4(0.1f, 0.3f, 0.5f);
			bgnode.ins.Strength.Value = 2.0f;

			background.AddNode(bgnode);
			bgnode.outs.Background.Connect(background.Output.ins.Surface);

			background.FinalizeGraph();

			scene.AddShader(background);
			scene.Background.Shader = background;
			scene.Background.Visibility = PathRay.PATH_RAY_ALL_VISIBILITY;
			
			/* get scene-specific default shader ID */
			var default_shader = scene.ShaderSceneId(scene.DefaultSurface);

			/* Set integrator settings */
			scene.Integrator.IntegratorMethod = IntegratorMethod.Path;
			scene.Integrator.MaxBounce = 8;
			scene.Integrator.MinBounce = 3;
			scene.Integrator.NoCaustics = true;
			scene.Integrator.MaxDiffuseBounce = 4;
			scene.Integrator.MaxGlossyBounce = 4;
			scene.Integrator.Seed = 1;
			scene.Integrator.SamplingPattern = SamplingPattern.Sobol;
			scene.Integrator.FilterGlossy = 0.0f;

			/* Add a bit of geometry and move camera around so we can see what we're rendering.
			 * First off we need an object, we put it at the origo
			 */
			var ob = CSycles.scene_add_object(Client.Id, scene.Id);
			CSycles.object_set_matrix(Client.Id, scene.Id, ob, Transform.Identity());
			/* Now we can create a mesh, attached to the object */
			var mesh = CSycles.scene_add_mesh(Client.Id, scene.Id, ob, default_shader);

			/* populate mesh with geometry */
			CSycles.mesh_set_verts(Client.Id, scene.Id, mesh, ref vert_floats, (uint)(vert_floats.Length / 3));
			var index_offset = 0;
			foreach (var face in nverts)
			{
				for (var j = 0; j < face - 2; j++)
				{
					var v0 = (uint)vertex_indices[index_offset];
					var v1 = (uint)vertex_indices[index_offset + j + 1];
					var v2 = (uint)vertex_indices[index_offset + j + 2];

					CSycles.mesh_add_triangle(Client.Id, scene.Id, mesh, v0, v1, v2, default_shader, false);
				}

				index_offset += face;
			}
			/* We continue with the session parameters. We initialise right away with
			 * useful settings, like tile size, samples to render, etc.
			 */
			var session_params = new SessionParameters(Client, dev)
			{
				Experimental = false,
				Samples = (int)samples,
				TileSize = new Size(64, 64),
				StartResolution = 64,
				Threads = 0, // setting this to 0 means automatically determine thread count based on cpu cores
				ShadingSystem = ShadingSystem.SVM,
				Background = true,
				ProgressiveRefine = false
			};

			/* We're ready to create a session. */
			Session = new Session(Client, session_params, scene);
			Session.Reset(width, height, samples);

			/* Set callbacks */
			g_update_callback = StatusUpdateCallback;
			g_update_render_tile_callback = SimpleRenderTileCallback;
			g_write_render_tile_callback = SimpleRenderTileCallback;

			Session.UpdateCallback = g_update_callback;
			Session.UpdateTileCallback = g_update_render_tile_callback;
			Session.WriteTileCallback = g_write_render_tile_callback;

			/* Now we can start rendering. */
			Session.Start();
			Session.Wait();

			/* And done. Retrieve image buffer and write out as a file. */
			uint bufsize;
			uint bufstride;
			CSycles.session_get_buffer_info(Client.Id, Session.Id, out bufsize, out bufstride);
			var pixels = CSycles.session_copy_buffer(Client.Id, Session.Id, bufsize);

			var bmp = new Bitmap((int)width, (int)height);
			for (var x = 0; x < width; x++)
			{
				for (var y = 0; y < height; y++)
				{
					var i = y * (int)width * 4 + x * 4;
					var r = ColorClamp((int)(pixels[i] * 255.0f));
					var g = ColorClamp((int)(pixels[i + 1] * 255.0f));
					var b = ColorClamp((int)(pixels[i + 2] * 255.0f));
					var a = ColorClamp((int)(pixels[i + 3] * 255.0f));
					bmp.SetPixel(x, y, Color.FromArgb(a, r, g, b));
				}
			}
			bmp.Save("test.bmp");

			/* Clean-up and we're done. */
			CSycles.shutdown();

		}
	}
}

Clone this wiki locally