MJHD

エモさ駆動開発

HSPでレイトレーシング

勉強も兼ねてHSPレイトレーシングを作った。

f:id:wait0000:20150823201417p:plain

Enter: レンダリング, Space: 粗くレンダリング, 十字キー: カメラの移動

できる限り直感的にベクトルの計算ができるよう、かなりマクロとか使いまくってるのでウンコード。

#module m

#define global INF expf(1000)

// Vector
#enum global X = 0
#enum global Y
#enum global Z

#define global let_vec(%1,%2,%3,%4) %1.X=%2:%1.Y=%3:%1.Z=%4
#define global copy_vec(%1,%2) let_vec %1,%2.X,%2.Y,%2.Z
#define global ret_vec(%1) let_vec %1,rx@m,ry@m,rz@m
#define global log_vec(%1,%2="Vec") logmes %2+strf("(%%f,%%f,%%f)",%1.X,%1.Y,%1.Z)

#define global plus_vec(%1,%2) _plus_vec %1.X,%1.Y,%1.Z,%2.X,%2.Y,%2.Z
#deffunc _plus_vec double x1,double y1,double z1,double x2,double y2,double z2
    rx=x1+x2
    ry=y1+y2
    rz=z1+z2
return

#define global minus_vec(%1,%2) _minus_vec %1.X,%1.Y,%1.Z,%2.X,%2.Y,%2.Z
#deffunc _minus_vec double x1,double y1,double z1,double x2,double y2,double z2
    rx=x1-x2
    ry=y1-y2
    rz=z1-z2
return


#define global cross_vec(%1,%2) _cross_vec %1.X,%1.Y,%1.Z,%2.X,%2.Y,%2.Z
#deffunc _cross_vec double x1,double y1,double z1,double x2,double y2,double z2
    rx=y1*z2-z1*y2
    ry=z1*x2-x1*z2
    rz=x1*y2-y1*x2
return

#define global ctype dot_vec(%1,%2) _dot_vec(%1.X,%1.Y,%1.Z,%2.X,%2.Y,%2.Z)
#defcfunc _dot_vec double x1,double y1,double z1,double x2,double y2,double z2
return x1*x2+y1*y2+z1*z2

#define global ctype mag_vec(%1) _mag_vec(%1.X,%1.Y,%1.Z)
#defcfunc _mag_vec double x1,double y1,double z1
return sqrt(_dot_vec(x1,y1,z1,x1,y1,z1))

#define global times_vec(%1,%2) _times_vec %1.X,%1.Y,%1.Z,%2
#deffunc _times_vec double x1,double y1,double z1,double n
    rx=n*x1
    ry=n*y1
    rz=n*z1
return

#define global norm_vec(%1) _norm_vec %1.X,%1.Y,%1.Z
#deffunc _norm_vec double x1,double y1,double z1
    mag = _mag_vec(x1,y1,z1)
    if (mag == 0) { div = INF }else{ div = 1.0 / mag }
    _times_vec x1,y1,z1, div
return


// Color
#enum global R = 0
#enum global G
#enum global B

#define global let_col(%1,%2,%3,%4) %1.R=%2:%1.G=%3:%1.B=%4
#define global copy_col(%1,%2) let_col %1,%2.R,%2.G,%2.B
#define global ret_col(%1) let_col %1,rr@m,rg@m,rb@m
#define global setDraw_col(%1) color limit(int(%1.R*255),0,255),limit(int(%1.G*255),0,255),limit(int(%1.B*255),0,255)
#define global log_col(%1,%2="Col") logmes %2+strf("(%%f,%%f,%%f)",%1.R,%1.G,%1.B)

#define global plus_col(%1,%2) _plus_col %1.R,%1.G,%1.B,%2.R,%2.G,%2.B
#deffunc _plus_col double r1,double g1,double b1,double r2,double g2,double b2
    rr=r1+r2
    rg=g1+g2
    rb=b1+b2
    _norm_result_col
return

#define global times_col(%1,%2) _times_col %1.R,%1.G,%1.B,%2
#deffunc _times_col double r1,double g1,double b1,double n
    rr=n*r1
    rg=n*g1
    rb=n*b1
    _norm_result_col
return

#define global cross_col(%1,%2) _cross_col %1.R,%1.G,%1.B,%2.R,%2.G,%2.B
#deffunc _cross_col double r1,double g1,double b1,double r2,double g2,double b2
    rr=r1*r2
    rg=g1*g2
    rb=b1*b2
    _norm_result_col
return

#deffunc _norm_result_col
    rr=limitf(rr,0,1.0):rg=limitf(rg,0,1.0):rb=limitf(rb,0,1.0)
return

#global

// Initialize
let_vec zero, 0.0,  0.0, 0.0
let_vec right, 1.0, 0.0, 0.0
let_vec left, -1.0, 0.0, 0.0
let_vec forward, 0.0, 0.0, 1.0
let_vec backward, 0.0, 0.0, -1.0
let_vec down, 0.0, -1.0, 0.0
let_vec up,   0.0,  1.0, 0.0

let_col white,1.0, 1.0, 1.0 
let_col black,0.0, 0.0, 0.0
let_col grey, 0.5, 0.5, 0.5
let_col red,  1.0, 0.0, 0.0
let_col green,0.0, 1.0, 0.0
let_col blue, 0.0, 0.0, 1.0

// Camera
let_vec camera,      3.0, 2.0, 4.0
let_vec cameraLook, -1.0, 0.5, 0.0

// Light
ddim light, 4, 3
ddim lightColor, 4, 3
let_vec light.0,    -2.0, 2.5, 0.0
let_vec light.1,     0.0, 3.5, 0.0
let_vec light.2,     1.5, 2.5, -1.5
let_vec light.3,     1.5, 2.5, 1.5
copy_col lightColor.0, white
copy_col lightColor.1, red
copy_col lightColor.2, blue
copy_col lightColor.3, green

// Sphere
ddim sphere, 2, 3
let_vec sphere.0,  0.0,  1.0, -0.25
let_vec sphere.1,  -1.0, 0.5, 1.5

// Ground
let_vec ground, 0.0, -1.0, 0.0

// Background Color
copy_col bgColor,  black

// Default Color
copy_col defColor, black

div   = 20

gosub *render

// Main Loop
*main
    
    gosub *keyCheck

    await 60

goto *main


*render
    
gosub *cameraInit

if (div > 1) :redraw 0

repeat dw

    dx = cnt
    
    if (div == 1) :redraw 0, dx*div, 0, (dx+1)*div, ginfo_winy

    repeat dh
    
        dy = cnt
    
        screenX =  (double(dx) - w/2.0) / 2.0 / w
        screenY = (-(double(dy) - h/2.0) / 2.0 / h) * aspect

        copy_vec cameraRayStart, camera
    
        times_vec cameraRgt, screenX      :ret_vec cameraRayDir
        times_vec cameraUp,  screenY      :ret_vec tmp
    
        plus_vec cameraRayDir, tmp        :ret_vec cameraRayDir
        plus_vec cameraRayDir, cameraFor  :ret_vec cameraRayDir
    
        norm_vec cameraRayDir             :ret_vec cameraRayDir
    
        gosub *trace
    
        setDraw_col cl
        boxf dx*div, dy*div, (dx+1)*div, (dy+1)*div
        
    loop
    
    if (div == 1) :redraw 1, dx*div, 0, (dx+1)*div, ginfo_winy
    
loop

if (div > 1) :redraw 1

return


*keyCheck
    
    stick key, 1+2+4+8
    
    // no needs to redraw
    if ((key & (1+2+4+8+32)) == 0) :return

    // initialize cameraDelta
    copy_vec cameraDelta, zero

    if (key & 1) {
        copy_vec cameraDelta, left
        div = 20
    }
    if (key & 2) {
        copy_vec cameraDelta, backward
        div = 20
    }
    if (key & 4) {
        copy_vec cameraDelta, right
        div = 20
    }
    if (key & 8) {
        copy_vec cameraDelta, forward
        div = 20
    }
    plus_vec camera, cameraDelta  :ret_vec camera

    if (key & 32) {
    
        div = 1
        
    }
    
    gosub *render
    
return

*cameraInit
    
    // initialize
    copy_vec cameraFor, zero
    copy_vec cameraRgt, zero
    copy_vec cameraUp , zero

    // determine axis of camera
    minus_vec cameraLook, camera   :ret_vec cameraFor
    norm_vec  cameraFor            :ret_vec cameraFor

    cross_vec cameraFor,  down     :ret_vec cameraRgt
    norm_vec  cameraRgt            :ret_vec cameraRgt

    cross_vec cameraFor,  cameraRgt:ret_vec cameraUp
    norm_vec  cameraUp             :ret_vec cameraUp


    dw = ginfo_winx / div
    dh = ginfo_winy / div
    
    w = double(dw)
    h = double(dh)
    
    aspect = h / w

return

*trace
    
    copy_col cl, bgColor
    
    repeat 5
    
        // setup ray
        copy_vec rayStart, cameraRayStart
        copy_vec rayDir,   cameraRayDir
            
        trace_mode = 1
        gosub *traceRay
    
        // doesn't hit
        if (distance >= INF) :break
    
        // Reflection
        times_vec normal, dot_vec(normal, rayDir) :ret_vec reflection
        times_vec reflection,   2.0               :ret_vec reflection
        minus_vec rayDir, reflection              :ret_vec reflection
        norm_vec  reflection                      :ret_vec reflection
    
        copy_vec  rayStart, rayPosition 
    
        // Lighting
        repeat length(light)
            minus_vec light.cnt,          rayStart     :ret_vec lightPosition
            norm_vec  lightPosition                    :ret_vec lightDir
        
            copy_vec rayDir,   lightDir
    
            trace_mode = 0
            gosub *traceRay
    
            // is in shadow
            if (distance <= mag_vec(lightPosition)) {
                continue
            }
    
            illum = dot_vec(lightDir, normal)
            spec  = dot_vec(lightDir, reflection)
        
            copy_col illumColor, defColor
            copy_col specColor,  defColor
    
            if (illum > 0) {
                times_col lightColor.cnt, illum             :ret_col illumColor
            }
            if (spec > 0) {
                times_col lightColor.cnt, powf(spec,250.0)  :ret_col specColor
            }
    
            cross_col illumColor, intersected     :ret_col illumColor
            cross_col specColor,  lightColor.cnt  :ret_col specColor
            plus_col  illumColor, specColor       :ret_col illumColor
            plus_col  cl, illumColor              :ret_col cl
        loop
    
        // setup next ray
        copy_vec cameraRayStart, rayStart
        copy_vec cameraRayDir,   reflection
    
    loop
    
    
return

*traceRay
    
    distance = INF
    
    // Sphere Intersection
    repeat length(sphere)
        minus_vec sphere.cnt, rayStart  :ret_vec eo
        v = dot_vec(eo, rayDir)
    
        if (v >= 0) {
            tmp_dist1 = 0.5 - (dot_vec(eo,eo) - v*v)
            if (tmp_dist1 >= 0) {

                tmp_dist2 = v - sqrt(tmp_dist1)
                
                if (distance > tmp_dist2) {
                    distance = tmp_dist2
    
                    times_vec rayDir,       distance     :ret_vec rayPosition
                    plus_vec  rayStart,     rayPosition  :ret_vec rayPosition
    
                    if (trace_mode == 1) {
                        copy_col intersected, grey
        
                        // normal
                        minus_vec rayPosition, sphere.cnt :ret_vec normal
                        norm_vec  normal                  :ret_vec normal
                    }
                }
            }
        }
    loop
    
    // Ground Intersection
    d = dot_vec(up, rayDir)
    if (d < 0) {
        tmp_dist1 = (dot_vec(up, rayStart) - ground.Y) / -d
        if (distance > tmp_dist1) {
            distance = tmp_dist1
    
            if (trace_mode == 1) {
                times_vec rayDir,       distance     :ret_vec rayPosition
                plus_vec  rayStart,     rayPosition  :ret_vec rayPosition
                
                if ((int(rayPosition.X) + int(rayPosition.Z)) \ 2 != 0) {
                    copy_col intersected, white
                } else {
                    copy_col intersected, black
                }
    
                // normal
                copy_vec normal, up
            }
        }
    }
    
return