mirror of
				https://github.com/stevenrobertson/cuburn.git
				synced 2025-11-03 18:00:55 -05:00 
			
		
		
		
	Arbitrary camera, part 1
This commit is contained in:
		
							
								
								
									
										34
									
								
								cuburn/affine.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								cuburn/affine.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,34 @@
 | 
				
			|||||||
 | 
					"""
 | 
				
			||||||
 | 
					Some simple operations on 2D affine matrices. These matrices are all stored
 | 
				
			||||||
 | 
					in row-major order, like C, instead of Fortran-style column-major storage.
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import numpy as np
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def from_flam3(a):
 | 
				
			||||||
 | 
					    """Convert from flam3-format [3][2] arrays to an affine matrix."""
 | 
				
			||||||
 | 
					    return np.matrix([ [a[0][0], a[1][0], a[2][0]]
 | 
				
			||||||
 | 
					                     , [a[0][1], a[1][1], a[2][1]]
 | 
				
			||||||
 | 
					                     , [0, 0, 1]])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def scale(x, y):
 | 
				
			||||||
 | 
					    return np.matrix([[x,0,0], [0,y,0], [0,0,1]])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def translate(x, y):
 | 
				
			||||||
 | 
					    return np.matrix([[1,0,x], [0,1,y], [0,0,1]])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def rotOrigin(rad):
 | 
				
			||||||
 | 
					    c = np.cos(rad)
 | 
				
			||||||
 | 
					    s = np.sin(rad)
 | 
				
			||||||
 | 
					    return np.matrix([[c, -s, 0], [s, c, 0], [0, 0, 1]])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def rotate(rad, x, y):
 | 
				
			||||||
 | 
					    """Rotates around the given point (x, y)."""
 | 
				
			||||||
 | 
					    return translate(x, y) * rotOrigin(rad) * translate(-x, -y)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def apply(m, x, y):
 | 
				
			||||||
 | 
					    """Apply matrix to point, returning new point as a tuple. Extends point
 | 
				
			||||||
 | 
					    to homogeneous coordinates before applying. Mostly here as an example."""
 | 
				
			||||||
 | 
					    r = m * np.matrix([x, y, 1]).T
 | 
				
			||||||
 | 
					    return r[0], r[1]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -39,7 +39,7 @@ __device__
 | 
				
			|||||||
void apply_xf{{xfid}}(float *ix, float *iy, float *icolor,
 | 
					void apply_xf{{xfid}}(float *ix, float *iy, float *icolor,
 | 
				
			||||||
                      const iter_info *info, mwc_st *rctx) {
 | 
					                      const iter_info *info, mwc_st *rctx) {
 | 
				
			||||||
    float tx, ty, ox = *ix, oy = *iy;
 | 
					    float tx, ty, ox = *ix, oy = *iy;
 | 
				
			||||||
    {{apply_affine('ox', 'oy', 'tx', 'ty', px, 'xf.c', 'pre')}}
 | 
					    {{apply_affine_flam3('ox', 'oy', 'tx', 'ty', px, 'xf.c', 'pre')}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ox = 0;
 | 
					    ox = 0;
 | 
				
			||||||
    oy = 0;
 | 
					    oy = 0;
 | 
				
			||||||
@ -97,7 +97,20 @@ void iter(mwc_st *msts, iter_info *infos, float *accbuf, float *denbuf) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        nsamps--;
 | 
					        nsamps--;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (x <= -0.5f || x >= 0.5f || y <= -0.5f || y >= 0.5f) {
 | 
					        // TODO: this may not optimize well, verify.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        float cx, cy;
 | 
				
			||||||
 | 
					        {{apply_affine('x', 'y', 'cx', 'cy', packer,
 | 
				
			||||||
 | 
					                       'cp.camera_transform', 'cam')}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        float ditherwidth = {{packer.get('0.5 * cp.spatial_filter_radius')}};
 | 
				
			||||||
 | 
					        float ditherx = mwc_next_11(&rctx) * ditherwidth;
 | 
				
			||||||
 | 
					        float dithery = mwc_next_11(&rctx) * ditherwidth;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        int ix = trunca(cx+ditherx), iy = trunca(cy+dithery);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (ix < 0 || ix >= {{features.width}} ||
 | 
				
			||||||
 | 
					            iy < 0 || iy >= {{features.height}} ) {
 | 
				
			||||||
            consec_bad++;
 | 
					            consec_bad++;
 | 
				
			||||||
            if (consec_bad > {{features.max_oob}}) {
 | 
					            if (consec_bad > {{features.max_oob}}) {
 | 
				
			||||||
                x = mwc_next_11(&rctx);
 | 
					                x = mwc_next_11(&rctx);
 | 
				
			||||||
@ -108,11 +121,7 @@ void iter(mwc_st *msts, iter_info *infos, float *accbuf, float *denbuf) {
 | 
				
			|||||||
            continue;
 | 
					            continue;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        float ditherx = mwc_next_11(&rctx) * 0.5f;
 | 
					        int i = iy * {{features.height}} + ix;
 | 
				
			||||||
        float dithery = mwc_next_11(&rctx) * 0.5f;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        int i = ((int)((y + 0.5f) * 1022.0f + ditherx) * 1024)
 | 
					 | 
				
			||||||
              +  (int)((x + 0.5f) * 1022.0f + dithery) + 1025;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // since info was declared const, C++ barfs unless it's loaded first
 | 
					        // since info was declared const, C++ barfs unless it's loaded first
 | 
				
			||||||
        float cp_step_frac = {{packer.get('cp_step_frac')}};
 | 
					        float cp_step_frac = {{packer.get('cp_step_frac')}};
 | 
				
			||||||
@ -127,8 +136,8 @@ void iter(mwc_st *msts, iter_info *infos, float *accbuf, float *denbuf) {
 | 
				
			|||||||
""")
 | 
					""")
 | 
				
			||||||
        return tmpl.substitute(
 | 
					        return tmpl.substitute(
 | 
				
			||||||
                features = self.features,
 | 
					                features = self.features,
 | 
				
			||||||
                packer = self.packer.view('info'))
 | 
					                packer = self.packer.view('info'),
 | 
				
			||||||
 | 
					                **globals())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def render(features, cps):
 | 
					def render(features, cps):
 | 
				
			||||||
    nsteps = 1000
 | 
					    nsteps = 1000
 | 
				
			||||||
 | 
				
			|||||||
@ -17,6 +17,18 @@ def assemble_code(*sections):
 | 
				
			|||||||
                      for kind in ['headers', 'decls', 'defs']])
 | 
					                      for kind in ['headers', 'decls', 'defs']])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def apply_affine(x, y, xo, yo, packer, base_accessor, base_name):
 | 
					def apply_affine(x, y, xo, yo, packer, base_accessor, base_name):
 | 
				
			||||||
 | 
					    return tempita.Template("""
 | 
				
			||||||
 | 
					    {{xo}} = {{packer.get(ba + '[0,0]', bn + '_xx')}} * {{x}}
 | 
				
			||||||
 | 
					           + {{packer.get(ba + '[0,1]', bn + '_xy')}} * {{y}}
 | 
				
			||||||
 | 
					           + {{packer.get(ba + '[0,2]', bn + '_xo')}};
 | 
				
			||||||
 | 
					    {{yo}} = {{packer.get(ba + '[1,0]', bn + '_yx')}} * {{x}}
 | 
				
			||||||
 | 
					           + {{packer.get(ba + '[1,1]', bn + '_yy')}} * {{y}}
 | 
				
			||||||
 | 
					           + {{packer.get(ba + '[1,2]', bn + '_yo')}};
 | 
				
			||||||
 | 
					    """).substitute(x=x, y=y, xo=xo, yo=yo, packer=packer,
 | 
				
			||||||
 | 
					                    ba=base_accessor, bn=base_name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def apply_affine_flam3(x, y, xo, yo, packer, base_accessor, base_name):
 | 
				
			||||||
 | 
					    """Read an affine transformation in *flam3 order* and apply it."""
 | 
				
			||||||
    return tempita.Template("""
 | 
					    return tempita.Template("""
 | 
				
			||||||
    {{xo}} = {{packer.get(ba + '[0][0]', bn + '_xx')}} * {{x}}
 | 
					    {{xo}} = {{packer.get(ba + '[0][0]', bn + '_xx')}} * {{x}}
 | 
				
			||||||
           + {{packer.get(ba + '[1][0]', bn + '_xy')}} * {{y}}
 | 
					           + {{packer.get(ba + '[1][0]', bn + '_xy')}} * {{y}}
 | 
				
			||||||
@ -42,6 +54,14 @@ uint32_t gtid() {
 | 
				
			|||||||
                (threadIdx.z + blockDim.z *
 | 
					                (threadIdx.z + blockDim.z *
 | 
				
			||||||
                    (blockIdx.x + (gridDim.x * blockIdx.y))));
 | 
					                    (blockIdx.x + (gridDim.x * blockIdx.y))));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					__device__
 | 
				
			||||||
 | 
					int trunca(float f) {
 | 
				
			||||||
 | 
					    // truncate as used in address calculations
 | 
				
			||||||
 | 
					    int ret;
 | 
				
			||||||
 | 
					    asm("cvt.rni.s32.f32    %0,     %1;" : "=r"(ret) : "f"(f));
 | 
				
			||||||
 | 
					    return ret;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
"""
 | 
					"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class DataPackerView(object):
 | 
					class DataPackerView(object):
 | 
				
			||||||
 | 
				
			|||||||
@ -9,10 +9,9 @@ from fr0stlib import pyflam3
 | 
				
			|||||||
from fr0stlib.pyflam3._flam3 import *
 | 
					from fr0stlib.pyflam3._flam3 import *
 | 
				
			||||||
from fr0stlib.pyflam3.constants import *
 | 
					from fr0stlib.pyflam3.constants import *
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from cuburn import affine
 | 
				
			||||||
from cuburn.variations import Variations
 | 
					from cuburn.variations import Variations
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Point = lambda x, y: np.array([x, y], dtype=np.double)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class Genome(pyflam3.Genome):
 | 
					class Genome(pyflam3.Genome):
 | 
				
			||||||
    @classmethod
 | 
					    @classmethod
 | 
				
			||||||
    def from_string(cls, *args, **kwargs):
 | 
					    def from_string(cls, *args, **kwargs):
 | 
				
			||||||
@ -26,6 +25,25 @@ class Genome(pyflam3.Genome):
 | 
				
			|||||||
        dens /= np.sum(dens)
 | 
					        dens /= np.sum(dens)
 | 
				
			||||||
        self.norm_density = [np.sum(dens[:i+1]) for i in range(len(dens))]
 | 
					        self.norm_density = [np.sum(dens[:i+1]) for i in range(len(dens))]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    scale = property(lambda cp: 2.0 ** cp.zoom)
 | 
				
			||||||
 | 
					    adj_density = property(lambda cp: cp.sample_density * (cp.scale ** 2))
 | 
				
			||||||
 | 
					    ppu = property(lambda cp: cp.pixels_per_unit * cp.scale)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def camera_transform(cp):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        An affine matrix which will transform IFS coordinates to image width
 | 
				
			||||||
 | 
					        and height. Assumes that width and height are constant.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        # TODO: when reading as a property during packing, this may be
 | 
				
			||||||
 | 
					        # calculated 6 times instead of 1
 | 
				
			||||||
 | 
					        return ( affine.translate(0.5 * cp.width, 0.5 * cp.height)
 | 
				
			||||||
 | 
					               * affine.scale(cp.ppu, cp.ppu)
 | 
				
			||||||
 | 
					               * affine.translate(-cp._center[0], -cp._center[1])
 | 
				
			||||||
 | 
					               * affine.rotate(cp.rotate * 2 * np.pi / 360,
 | 
				
			||||||
 | 
					                               cp.rot_center[0],
 | 
				
			||||||
 | 
					                               cp.rot_center[1]) )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Animation(object):
 | 
					class Animation(object):
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    Control structure for rendering a series of frames.
 | 
					    Control structure for rendering a series of frames.
 | 
				
			||||||
@ -86,6 +104,9 @@ class Features(object):
 | 
				
			|||||||
        if any(lambda cp: cp.final_xform_enable):
 | 
					        if any(lambda cp: cp.final_xform_enable):
 | 
				
			||||||
            raise NotImplementedError("Final xform")
 | 
					            raise NotImplementedError("Final xform")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.width = genomes[0].width
 | 
				
			||||||
 | 
					        self.height = genomes[0].height
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class XFormFeatures(object):
 | 
					class XFormFeatures(object):
 | 
				
			||||||
    def __init__(self, xforms, xform_id):
 | 
					    def __init__(self, xforms, xform_id):
 | 
				
			||||||
        self.id = xform_id
 | 
					        self.id = xform_id
 | 
				
			||||||
@ -96,27 +117,4 @@ class XFormFeatures(object):
 | 
				
			|||||||
            self.vars = (
 | 
					            self.vars = (
 | 
				
			||||||
                self.vars.union(set([i for i, v in enumerate(x.var) if v])))
 | 
					                self.vars.union(set([i for i, v in enumerate(x.var) if v])))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Camera(object):
 | 
					 | 
				
			||||||
    """Viewport and exposure."""
 | 
					 | 
				
			||||||
    def __init__(self, frame, cp):
 | 
					 | 
				
			||||||
        # Calculate the conversion matrix between the IFS space (xform
 | 
					 | 
				
			||||||
        # coordinates) and the sampling lattice (bucket addresses)
 | 
					 | 
				
			||||||
        # TODO: test this code (against compute_camera?)
 | 
					 | 
				
			||||||
        scale = 2.0 ** cp.zoom
 | 
					 | 
				
			||||||
        self.sample_density = cp.sample_density * scale * scale
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        center = Point(cp._center[0], cp._center[1])
 | 
					 | 
				
			||||||
        size = Point(cp.width, cp.height)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # pix per unit, where 'unit' is '1.0' in IFS space
 | 
					 | 
				
			||||||
        self.ppu = Point(
 | 
					 | 
				
			||||||
            cp.pixels_per_unit * scale / frame.pixel_aspect_ratio,
 | 
					 | 
				
			||||||
            cp.pixels_per_unit * scale)
 | 
					 | 
				
			||||||
        cornerLL = center - (size / (2 * self.ppu))
 | 
					 | 
				
			||||||
        self.lower_bounds = cornerLL - gutter
 | 
					 | 
				
			||||||
        self.upper_bounds = cornerLL + (size / self.ppu) + gutter
 | 
					 | 
				
			||||||
        self.norm_scale = 1.0 / (self.upper_bounds - self.lower_bounds)
 | 
					 | 
				
			||||||
        self.norm_offset = -self.norm_scale * self.lower_bounds
 | 
					 | 
				
			||||||
        self.idx_scale = size * self.norm_scale
 | 
					 | 
				
			||||||
        self.idx_offset = size * self.norm_offset
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user