-
Notifications
You must be signed in to change notification settings - Fork 17
.NET API
jesterKing edited this page Oct 22, 2014
·
3 revisions
CSycles is the .NET API to Cycles. The following shows a minimal example of a program to create a rendered image with Cycles.
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();
}
}
}