22、图像基光照:原理、代码与应用

图像基光照:原理、代码与应用

1. 局部场景渲染与差分渲染

在局部场景渲染中,我们将局部场景多边形的颜色定义为:
[
d_{col} =
\begin{pmatrix}
\frac{R}{R’} & \frac{G}{G’} & \frac{B}{B’}
\end{pmatrix}
]
并且 $k_d$ 系数需保持为 1。

差分渲染是一种用于渲染局部场景的方法。即使局部场景使用恒定的双向反射分布函数(BRDF)进行建模,且背景图像具有一些带纹理外观的漫反射,该方法也能产生可接受的结果。其原理是通过从局部场景背景图像中减去无合成对象时渲染的局部场景与有合成对象时渲染的局部场景之间的差异,来计算局部场景中各点的颜色。具体公式如下:
[
LS_{final} = LS_b - (LS_{nobj} - LS_{obj})
]
或者等价表示为:
[
LS_{final} = LS_b + (LS_{obj} - LS_{nobj})
]
其中,$LS_{final}$ 是局部场景最终图像的颜色,$LS_b$ 是背景图像中局部场景的颜色,$LS_{obj}$ 是与合成对象交互渲染的局部场景的颜色,$LS_{nobj}$ 是无合成对象时渲染的局部场景的颜色。

2. 代码模块

2.1 HDR 图像 API

HDR 图像 API 提供了一系列用于处理 HDR 图像的函数,如下表所示:
| 函数名 | 功能 |
| ---- | ---- |
| HDRImage *hdrimg_init(int w, int h); | 创建分辨率为 $w \times h$ 的 HDR 图像 |
| void hdrimg_free( HDRImage *img ); | 释放图像内存 |
| void hdrimg_write(HDRImage *i, char *fname); | 将 HDR 图像保存为 PFM 文件 |
| HDRImage *hdrimg_read(char *fname); | 从 PFM 文件读取 HDR 图像 |
| Color hdrimg_getc( HDRImage *p, int u, int v ); | 获取指定像素的颜色辐射度 |
| void hdrimg_putc( HDRImage *p, int u, int v, Color c ); | 修改指定像素的颜色辐射度 |

2.2 HDR 图像代码

以下是 HDR 图像代码的实现:

// ibl/hdr.h
#ifndef HDR_H
#define HDR_H
#include "color.h"
#include "image.h"
#include "string.h"
typedef struct HDRImage{
    int w, h;
    Color *c;
}HDRImage;
HDRImage *hdrimg_init( int w, int h);
void hdrimg_free( HDRImage *img );
void hdrimg_write(HDRImage *i, char *fname);
HDRImage *hdrimg_read(char *fname);
Color hdrimg_getc( HDRImage *p, int u, int v );
void hdrimg_putc( HDRImage *p, int u, int v, Color c );
#endif

// ibl/hdr.c
#include "hdr.h"
static void read_line( FILE *f, char *s );
HDRImage *hdrimg_init( int w, int h )
{
    HDRImage *img = NEWSTRUCT(HDRImage);
    img->w = w;
    img->h = h;
    img->c = NEWTARRAY( w*h, Color );
    return img;
}
void hdrimg_free( HDRImage *img )
{
    free( img->c );
    free( img );
}
Color hdrimg_getc( HDRImage *img, int x, int y )
{
    return img->c[img->w*y + x];
}
void hdrimg_putc( HDRImage *img, int x, int y, Color c )
{
    img->c[img->w*y + x] = c;
}
void hdrimg_write(HDRImage *img, char *fname)
{
    Color c;
    float cx, cy, cz;
    char s[10];
    int x, y, xres, yres;
    FILE *f = fopen( fname, "wb" );
    fwrite( "PF\n", 3, 1, f );
    sprintf( s, "%i %i\n", img->w, img->h );
    fwrite( s, strlen(s), 1, f );
    sprintf( s, "-1.0\n" );
    fwrite( s, strlen(s), 1, f );
    for ( y = 0; y < img->h; y++ )
        for ( x = 0; x < img->w; x++ ){
            c = hdrimg_getc( img, x, y );
            cx = (float)c.x;
            fwrite( &cx, sizeof(float), 1, f );
            cy = (float)c.y;
            fwrite( &cy, sizeof(float), 1, f );
            cz = (float)c.z;
            fwrite( &cz, sizeof(float), 1, f );
        }
    fclose(f);
}
HDRImage *hdrimg_read(char *fname)
{
    float r, g, b, v;
    char s[100];
    FILE *f = fopen( fname, "rb" );
    read_line( f, s );
    if( strcmp( s, "PF\n" ) )
        error("wrong PFM File Format\n" );
    read_line( f, s );
    sscanf( s, "%d %d\n", &xres, &yres );
    read_line( f, s );
    sscanf( s, "%f", &v );
    if( v != -1 )
        error("wrong PFM File Format\n" );
    img = hdrimg_init( xres, yres );
    if( v == -1 ){
        for( y = 0; y < img->h; y++ )
            for( x = 0; x < img->w; x++ ){
                fread( &r, sizeof(float), 1, f );
                fread( &g, sizeof(float), 1, f );
                fread( &b, sizeof(float), 1, f );
                hdrimg_putc( img, x, y, c_make(r, g, b) );
            }
    }
    fclose(f);
    return img;
}
void read_line( FILE *f, char *s )
{
    int i = 0;
    do{
        fread( &s[i], 1, 1, f );
    }
    while( s[i++] != 0x0A );
    s[i] = 0x00;
}

2.3 图像基光照 API

图像基光照 API 提供了一系列用于处理 HDR 穹顶和多边形阴影的函数,如下表所示:
| 函数名 | 功能 |
| ---- | ---- |
| void hdrdome_init( void ); | 初始化场景的 HDR 穹顶为 NULL |
| HDRDome *hdrdome_alloc( Vector3 orig, Vector3 south, HDRImage *img ); | 创建具有指定方向的辐射穹顶 |
| void hdrdome_free( HDRDome *d ); | 释放辐射穹顶的内存 |
| Color hdrdome_value( HDRDome *d, Vector3 v ); | 获取穹顶在指定方向的三色 HDR 颜色 |
| Val hdrdome_parse(int pass, Pval *pl); | 实现 HDR 穹顶场景描述语言的解析函数 |
| PolyShadow *plshadow_alloc( Material *m, Hpoly *p, HDRImage *img ); | 创建具有指定几何、材质和背景图像的多边形阴影 |
| void plshadow_free( PolyShadow *ps ); | 销毁多边形阴影 |
| Val plshadow_parse(int pass, Pval *plist); | 解析场景描述语言中的多边形阴影 |
| Object *plshadow_to_obj( PolyShadow *ps ); | 将多边形阴影转换为对象 |

2.4 图像基光照代码

以下是图像基光照代码的实现:

// ibl/ibl.h
#ifndef IBL_H
#define IBL_H
#include "color.h"
#include "hdr.h"
#include "lang.h"
#include "shade.h"
#include "poly.h"
#include "obj.h"
#include <string.h>
#define REAL_RANDOM
((double)rand()/RAND_MAX)
typedef struct HDRDome{
    Vector3 u;
    Vector3 v;
    Vector3 n;
    HDRImage *img;
}HDRDome;
typedef struct PolyShadow{
    struct PolyShadow *next;
    Hpoly *p;
    Material *m;
    HDRImage *img;
}PolyShadow;
HDRDome *hdr_dome;
/* HDR Dome Functions */
void hdrdome_init( void );
HDRDome *hdrdome_alloc( Vector3 orig, Vector3 south,
                        HDRImage *img );
void hdrdome_free( HDRDome *d );
Color hdrdome_value( HDRDome *d, Vector3 v );
Val hdrdome_parse(int pass, Pval *pl);
/* Poly Shadow Functions */
PolyShadow *plshadow_alloc( Material *m, Hpoly *p, HDRImage *img );
void plshadow_free( PolyShadow *ps );
Val plshadow_parse(int pass, Pval *plist);
Object *plshadow_to_obj( PolyShadow *ps );
#endif

// ibl/ibl.c
#include "ibl.h"
static Color hdrmap_value( HDRDome *d, Vector3 v );
static Vector3 change_ref( Vector3 v, HDRDome *d );
void hdrdome_init( void )
{
    hdr_dome = NULL;
}
HDRDome *hdrdome_alloc( Vector3 orig, Vector3 south, HDRImage *img )
{
    HDRDome *d = NEWSTRUCT( HDRDome );
    d->n = v3_unit( south );
    d->u = v3_unit( v3_sub( orig,
                            v3_scale( v3_dot( orig, d->n ), d->n )) );
    d->v = v3_cross( d->n, d->u );
    d->img = img;
    return d;
}
void hdrdome_free( HDRDome *d )
{
    hdrimg_free( d->img );
    free( d );
}
Color hdrdome_value( HDRDome *d, Vector3 v )
{
    Real theta, phi;
    v = change_ref( v, d );
    theta = acos(v.z);
    phi = atan2(v.y,v.x);
    return hdrimg_getc( d->img, ROUND((1 - (phi/PI))*d->img->w/2 ),
                        ROUND((theta/PI)*d->img->h) );
}
Vector3 change_ref( Vector3 v, HDRDome *d )
{
    Matrix4 m;
    m = m4_ident();
    m.r1.x = d->u.x;
    m.r1.y = d->v.x;
    m.r1.z = d->n.x;
    m.r2.x = d->u.y;
    m.r2.y = d->v.y;
    m.r2.z = d->n.y;
    m.r3.x = d->u.z;
    m.r3.y = d->v.z;
    m.r3.z = d->n.z;
    return v3_m3mult( v, m );
}
// ibl/plshadow.c
#include "ibl.h"
PolyShadow *plshadow_alloc( Material *m, Hpoly *p, HDRImage *img )
{
    PolyShadow *ps;
    ps = NEWSTRUCT(PolyShadow);
    ps->m = m;
    ps->p = p;
    ps->img = img;
    return ps;
}
void plshadow_free( PolyShadow *ps )
{
    free( ps->m );
    free( ps->p );
    free( ps->img );
    free( ps );
}
Object *plshadow_to_obj( PolyShadow *ps )
{
    Shape *sh = shape_new(V_HPOLYLIST, ps->p);
    Object *o = obj_new(V_HPOLYLIST, sh);
    o->mat = ps->m;
    return o;
}
// Extracted from lang/sdltypes.h
#define V_PL_LIGHT 501
#define V_HDR_DOME 502
#define V_HDR_SCENE 503
#define V_PL_SHADOW 504
// ibl/lang.c
#include "ibl.h"
#include "sdltypes.h"
Val hdrdome_parse(int pass, Pval *pl)
{
    Pval *p;
    Val v = {V_NULL, 0};
    if (pass == T_EXEC) {
        Vector3 o, s;
        HDRDome *dome;
        o = pvl_get_v3(pl, "orig", v3_make(1,0,0));
        s = pvl_get_v3(pl, "south", v3_make(0,-1,0));
        p = pl;
        while (p != NULL) {
            if (p->name && strcmp(p->name,"hdrimg") == 0 && p->val.type == V_STR)
                dome = hdrdome_alloc( o, s, hdrimg_read( (char*)p->val.u.v ) );
            p = p->next;
        }
        v.type = V_HDR_DOME;
        v.u.v = dome;
        hdr_dome = (HDRDome*)dome;
    }
    return v;
}
Val plshadow_parse(int pass, Pval *plist)
{
    struct Hpoly *pols;
    Material *m;
    HDRImage *img = NULL;
    PolyShadow *plshadow;
    Val v = {V_NULL, 0};
    if (pass == T_EXEC) {
        Pval *p;
        for (p = plist; p !=NULL; p = p->next) {
            if (p->name == NULL) {
                error("(shadow) syntax error");
            } else if (strcmp(p->name, "material") == 0 && p->val.type == V_MATERIAL) {
                m = p->val.u.v;
            } else if (strcmp(p->name, "shape") == 0 && p->val.type == V_HPOLYLIST) {
                pols = p->val.u.v;
            } else if( strcmp(p->name,"img") == 0 && p->val.type == V_STR ){
                img = hdrimg_read( (char*)p->val.u.v );
            } else error("(shadow) syntax error");
        }
        plshadow = plshadow_alloc( m, pols, img );
        v.type = V_PL_SHADOW;
        v.u.v = plshadow;
    }
    return v;
}

2.5 HDR 场景 API

HDR 场景 API 提供了一系列用于处理 HDR 场景的函数,如下表所示:
| 函数名 | 功能 |
| ---- | ---- |
| Val hdrscene_parse(int c, Pval *pl); | 解析 HDR 场景 |
| HDRScene *hdrscene_read(void); | 读取 HDR 场景 |
| HDRScene *hdrscene_eval(void); | 评估 HDR 场景 |
| void hdrscene_free(HDRScene *s); | 释放 HDR 场景的内存 |

2.6 HDR 场景代码

以下是 HDR 场景代码的实现:

// hdrscene/hdrscene.h
#ifndef HDR_SCENE_H
#define HDR_SCENE_H
#include <stdio.h>
#include "lang.h"
#include "obj.h"
#include "sdltypes.h"
#include "view.h"
#include "scene.h"
#include "hdr.h"
#include "ibl.h"
#include "plight.h"
#include "ibl.h"
typedef struct HDRScene {
    struct View *view;
    struct HDRImage *hdrimg;
    struct HDRDome *hdrdome;
    struct PolyLight *plights;
    struct PolyShadow *plshadow;
    struct Object *objs;
} HDRScene;
Val hdrscene_parse(int c, Pval *pl);
HDRScene *hdrscene_read(void);
HDRScene *hdrscene_eval(void);
void hdrscene_free(HDRScene *s);
#endif

// hdrscene/hdrscene.c
#include "hdrscene.h"
static struct View *view = NULL;
static struct HDRImage *hdrimg = NULL;
static struct HDRDome *hdrdome = NULL;
static struct PolyLight *plights = NULL;
static struct PolyShadow *plshadow = NULL;
static struct Object *objs = NULL;
#ifndef LIST_INSERT
#define LIST_INSERT(L, I, TYPE) {struct TYPE *tt = I; tt->next = L; L = tt; }
#endif
static void hdrcollect_items(Pval *pl)
{
    Pval *p = pl;
    while (p != NULL) {
        if (p->name && strcmp(p->name,"object") == 0 && p->val.type == V_OBJECT)
            objs = obj_insert(objs, p->val.u.v);
        else if (p->name && strcmp(p->name,"object") == 0 && p->val.type == V_GROUP)
            objs = obj_list_insert(objs, p->val.u.v);
        else if (p->name && strcmp(p->name,"camera") == 0 && p->val.type == V_CAMERA)
            view = p->val.u.v;
        else if (p->name && strcmp(p->name,"dome") == 0 && p->val.type == V_HDR_DOME)
            hdrdome = p->val.u.v;
        else if (p->name && strcmp(p->name,"light") == 0 && p->val.type == V_PL_LIGHT)
            LIST_INSERT(plights, p->val.u.v, PolyLight)
        else if(p->name && strcmp(p->name,"shadow") == 0 && p->val.type == V_PL_SHADOW)
            LIST_INSERT(plshadow, p->val.u.v, PolyShadow)
        p = p->next;
    }
}
Val hdrscene_parse(int c, Pval *pl)
{
    Val v = {V_NULL, 0};
    if (c == T_EXEC) {
        HDRScene *s = NEWSTRUCT(HDRScene);
        view = NULL; hdrimg = NULL; plights = NULL; objs = NULL;
        hdrcollect_items(pl);
        s->view = view;
        s->hdrimg = hdrimg;
        s->hdrdome = hdrdome;
        s->objs = objs;
        s->plights = plights;
        s->plshadow = plshadow;
        v.type = V_HDR_SCENE;
        v.u.v = s;
    }
    return v;
}
HDRScene *hdrscene_read(void)
{
    if (lang_parse() == 0)
        return hdrscene_eval();
    else
        error("(hdrscene read)");
}
HDRScene *hdrscene_eval(void)
{
    HDRScene *s;
    Val v = lang_nd_eval();
    if (v.type != V_HDR_SCENE)
        error("(hdrscene eval)");
    else
        s = v.u.v;
    if (s->view == NULL)
        s->view = initview();
    if (s->hdrimg == NULL)
        s->hdrimg = hdrimg_init( s->view->sc.ur.x, s->view->sc.ur.y );
    return s;
}
void hdrscene_free(HDRScene *s)
{
    if (s->objs)
        obj_list_free(s->objs);
    if (s->view)
        efree(s->view);
    if (s->hdrimg)
        hdrimg_free(s->hdrimg);
    if (s->hdrdome)
        hdrdome_free(s->hdrdome);
    if (s->plights)
        plight_free(s->plights);
    if(s->plshadow)
        plshadow_free(s->plshadow);
    efree(s);
}

2.7 穹顶路径追踪 API

穹顶路径追踪 API 提供了两个用于实现路径追踪算法的函数,如下表所示:
| 函数名 | 功能 |
| ---- | ---- |
| Color dome_trace_path( int first_level, int level, Ray v, Object *ol ); | 实现带有辐射穹顶的路径追踪算法 |
| Color dome_mirror_reflect( int first_level, int level, Ray v, Inode *i, Vector3 p, Object *ol, Material *m ); | 返回完美镜面反射方向的颜色 |

2.8 穹顶路径追踪代码

以下是穹顶路径追踪代码的实现:

// Extracted from lptrace/ptrace.h
Color dome_trace_path( int first_level, int level, Ray v, Object *ol );
Color dome_mirror_reflect( int first_level, int level, Ray v, Inode *i,
                           Vector3 p, Object *ol, Material *m );
// lptrace/dmptrace.c
#include "ptrace.h"
Color dome_trace_path( int first_level, int level, Ray v, Object *ol )
{
    HDRDome *dome = hdr_dome;
    Color c = C_BLACK, c_aux, w;
    Ray r;
    Inode *i = ray_intersect(ol, v);
    adjust_normal( i, v );
    if((i != NULL) && level){
        level--;
        Material *m = i->m;
        Vector3 p = ray_point(v, i->t);
        Cone recv = cone_make(p, i->n, PIOVER2);
        Vector3 d = sort_new_direction(recv);
        if( m->se < 0 )
            c = dome_mirror_reflect( first_level, level, v, i, p, ol, m );
        else{
            c_aux = dome_trace_path( first_level, level, ray_make(p, d), ol );
            c = apply_bphong( m, c_aux, i->n,
                              d, v3_scale(-1,v.d),
                              2*PI*v3_dot(d, i->n) );
        }
        inode_free(i);
        return c;
    }
    else{
        if(( i == NULL ) && (level == first_level ) ){
            return C_BLACK;
        }
        else{
            if( i == NULL )
                return hdrdome_value( dome, v.d );
            if( i != NULL )
                inode_free(i);
            return C_BLACK;
        }
    }
}
// Extracted from lptrace/aux.c
Color dome_mirror_reflect( int first_level, int level, Ray v, Inode *i,
                           Vector3 p, Object *ol, Material *m )
{
    Vector3 d, c_aux;
    d = v3_sub( v.d, v3_scale( 2*v3_dot( v.d, i->n ), i->n ) );
    c_aux = dome_trace_path( first_level, level, ray_make(p, d), ol );
    return v3_scale( m->ks, c_aux );
}

3. 多边形阴影颜色调整程序

多边形阴影颜色调整程序可用于调整场景中捕获阴影的多边形的 $d_{col}$ 参数。使用该程序时,场景描述中的 plshadow 命令的 $k_d$ 需设置为 1,$d_{col}$ 可设置为任意值。程序运行后,会在屏幕上打印出应替换 $d_{col}$ 向量的值,同时需保持 $k_d$ 为 1。

3.1 程序输入

  • argv[1] :包含相机内参的文件名称。
  • argv[2] :包含每个帧外参的文件名称。
  • argv[3] :用于运行程序的帧号,需选择无对象遮挡捕获阴影多边形的帧。
  • stdin :以场景描述语言编码的场景。

3.2 程序假设

程序假设待处理的视频已拆分为名为 bk%d.pfm 的帧,其中 %d 为整数。

3.3 代码实现

// getcolor/main.h
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <ctype.h>
#include "image.h"
#include "defs.h"
#include "geom.h"
#include "stack.h"
#include "view.h"
#include "poly.h"
#include "prim.h"
#include "hier.h"
#include "lang.h"
#include "clip.h"
#include "raster.h"
#include "shade.h"
#include "ray.h"
#include "csg.h"
#include "rt.h"
#include "rshade.h"
#include "hdrscene.h"
#include "ptrace.h"
#include "ibl.h"
#include "mmove.h"
void init_render(void);
Ray ray_view(int u, int v);
void init_lang(void);
// getcolor/main.c
#include "main.h"
static HDRScene *s;
static Matrix4 mclip, mdpy;
#define MAX_NFRAMES 1000
#define MAX_PTRACE_SAMPLES 100
#define PATH_SIZE 1
#define MAX_N 10000
void main( int argc, char **argv )
{
    Real sl;
    Color bk, c_aux, c_med, bk_med;
    int u, v, smpl, ll_y, ur_y;
    long int n;
    Ray r;
    Object *obj;
    CamData *cd;
    HDRImage *aux;
    char str[50];
    srand(time(NULL));
    hdrdome_init();
    init_lang();
    s = hdrscene_read();
    aux = hdrimg_read( "bk0.pfm" );
    cd = cam_data_alloc( MAX_NFRAMES, aux->w, aux->h, .01, 1000. );
    mmove_read( cd, argv[1], argv[2] );
    mmove_view( s->view, cd, atoi( argv[3] ) );
    init_render();
    sprintf( str, "bk%i.pfm", atoi( argv[3] ) );
    s->plshadow->img = hdrimg_read( str );
    ll_y = s->view->sc.ll.y;
    ur_y = s->view->sc.ur.y;
    obj = plshadow_to_obj( s->plshadow );
    n = 0;
    c_med = C_BLACK;
    bk_med = C_BLACK;
    for(v = ll_y; v < ur_y; v += 1) {
        for (u = s->view->sc.ll.x; u < s->view->sc.ur.x; u += 1) {
            r = ray_unit(ray_transform(ray_view(u, v), mclip));
            bk = hdrimg_getc( s->plshadow->img, u, v );
            for( smpl = 0; smpl < MAX_PTRACE_SAMPLES; smpl++ ){
                if( (hit_surface( r, obj )) && (!hit_surface( r, s->objs )) &&
                    (s->plshadow != NULL) && (s->plshadow->img != NULL) &&
                    (n < MAX_N) ){
                    c_aux = dome_trace_path(PATH_SIZE, PATH_SIZE, r, obj );
                    c_med = v3_add( v3_scale( ((Real)n)/(n+1), c_med ),
                                    v3_scale( 1./(n+1), c_aux ));
                }
            }
        }
    }
}

综上所述,本文介绍了局部场景渲染的差分渲染方法,以及一系列用于处理 HDR 图像、图像基光照、HDR 场景和穹顶路径追踪的代码模块和 API。同时,还介绍了多边形阴影颜色调整程序的使用方法和代码实现。这些技术和代码可以帮助我们实现更真实的图像基光照效果。

4. 代码模块关系与工作流程

4.1 代码模块关系图

下面通过 mermaid 流程图展示各个代码模块之间的关系:

graph LR
    classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px;

    A[HDR图像模块]:::process --> B[图像基光照模块]:::process
    B --> C[HDR场景模块]:::process
    C --> D[穹顶路径追踪模块]:::process
    A --> E[多边形阴影颜色调整程序]:::process
    B --> E
    C --> E

从这个流程图可以看出,各个模块之间存在着紧密的依赖关系。HDR 图像模块是基础,为其他模块提供了处理 HDR 图像的基本功能。图像基光照模块依赖于 HDR 图像模块,用于处理 HDR 穹顶和多边形阴影。HDR 场景模块则整合了图像基光照模块和 HDR 图像模块的功能,用于处理整个 HDR 场景。穹顶路径追踪模块依赖于 HDR 场景模块,实现了带有辐射穹顶的路径追踪算法。多边形阴影颜色调整程序则综合使用了前面几个模块的功能,用于调整场景中捕获阴影的多边形的颜色参数。

4.2 工作流程分析

4.2.1 初始化阶段
  1. HDR 图像模块初始化 :调用 hdrimg_init 函数创建 HDR 图像,为后续处理准备图像数据结构。
  2. 图像基光照模块初始化 :调用 hdrdome_init 函数初始化场景的 HDR 穹顶为 NULL。
  3. HDR 场景模块初始化 :调用 hdrscene_read 函数读取 HDR 场景,解析场景描述语言,构建 HDR 场景的数据结构。
4.2.2 渲染阶段
  1. 穹顶路径追踪 :调用 dome_trace_path 函数实现带有辐射穹顶的路径追踪算法,计算场景中各点的颜色。
  2. 图像渲染 :根据路径追踪的结果,调用 hdrimg_putc 函数将颜色信息写入 HDR 图像。
4.2.3 后处理阶段
  1. 多边形阴影颜色调整 :运行多边形阴影颜色调整程序,调整场景中捕获阴影的多边形的颜色参数。
  2. 图像保存 :调用 hdrimg_write 函数将最终的 HDR 图像保存为 PFM 文件。

5. 关键技术点分析

5.1 差分渲染技术

差分渲染通过从局部场景背景图像中减去无合成对象时渲染的局部场景与有合成对象时渲染的局部场景之间的差异,来计算局部场景中各点的颜色。这种方法的优点在于,即使局部场景使用恒定的 BRDF 进行建模,也能在背景图像具有带纹理外观的漫反射时产生可接受的结果。其核心公式为:
[
LS_{final} = LS_b - (LS_{nobj} - LS_{obj}) = LS_b + (LS_{obj} - LS_{nobj})
]
该公式的实现步骤如下:
1. 分别渲染无合成对象的局部场景和有合成对象的局部场景,得到 $LS_{nobj}$ 和 $LS_{obj}$。
2. 计算两者的差异 $LS_{nobj} - LS_{obj}$。
3. 从背景图像的局部场景颜色 $LS_b$ 中减去这个差异,得到最终的局部场景颜色 $LS_{final}$。

5.2 穹顶路径追踪算法

穹顶路径追踪算法是一种用于模拟光线在场景中传播和反射的算法。该算法通过递归的方式,不断追踪光线与场景中对象的交互,计算出最终的颜色。在穹顶路径追踪算法中,使用了辐射穹顶来模拟场景周围的光照环境。

5.2.1 算法流程
  1. 光线与对象相交检测 :调用 ray_intersect 函数检测光线与场景中对象的交点。
  2. 材质处理 :根据交点处的材质属性,判断光线的反射类型。如果材质的 se 参数为负,则进行镜面反射;否则,进行漫反射。
  3. 递归追踪 :对于漫反射,随机选择一个新的方向,继续追踪光线;对于镜面反射,计算反射方向,继续追踪光线。
  4. 颜色计算 :根据光线的交互结果,计算出最终的颜色。
5.2.2 代码实现
Color dome_trace_path( int first_level, int level, Ray v, Object *ol )
{
    HDRDome *dome = hdr_dome;
    Color c = C_BLACK, c_aux, w;
    Ray r;
    Inode *i = ray_intersect(ol, v);
    adjust_normal( i, v );
    if((i != NULL) && level){
        level--;
        Material *m = i->m;
        Vector3 p = ray_point(v, i->t);
        Cone recv = cone_make(p, i->n, PIOVER2);
        Vector3 d = sort_new_direction(recv);
        if( m->se < 0 )
            c = dome_mirror_reflect( first_level, level, v, i, p, ol, m );
        else{
            c_aux = dome_trace_path( first_level, level, ray_make(p, d), ol );
            c = apply_bphong( m, c_aux, i->n,
                              d, v3_scale(-1,v.d),
                              2*PI*v3_dot(d, i->n) );
        }
        inode_free(i);
        return c;
    }
    else{
        if(( i == NULL ) && (level == first_level ) ){
            return C_BLACK;
        }
        else{
            if( i == NULL )
                return hdrdome_value( dome, v.d );
            if( i != NULL )
                inode_free(i);
            return C_BLACK;
        }
    }
}

5.3 多边形阴影颜色调整技术

多边形阴影颜色调整程序可用于调整场景中捕获阴影的多边形的 $d_{col}$ 参数。该程序的核心思想是通过路径追踪算法计算出多边形在阴影区域的颜色,然后根据计算结果调整 $d_{col}$ 参数。

5.3.1 操作步骤
  1. 输入参数设置 :设置相机内参文件、帧外参文件和帧号。
  2. 场景读取 :调用 hdrscene_read 函数读取 HDR 场景。
  3. 路径追踪 :对于场景中的每个像素,调用 dome_trace_path 函数进行路径追踪,计算出该像素的颜色。
  4. 颜色调整 :根据路径追踪的结果,调整多边形的 $d_{col}$ 参数。
  5. 结果输出 :将调整后的 $d_{col}$ 参数打印到屏幕上。
5.3.2 代码实现
// getcolor/main.c
#include "main.h"
static HDRScene *s;
static Matrix4 mclip, mdpy;
#define MAX_NFRAMES 1000
#define MAX_PTRACE_SAMPLES 100
#define PATH_SIZE 1
#define MAX_N 10000
void main( int argc, char **argv )
{
    Real sl;
    Color bk, c_aux, c_med, bk_med;
    int u, v, smpl, ll_y, ur_y;
    long int n;
    Ray r;
    Object *obj;
    CamData *cd;
    HDRImage *aux;
    char str[50];
    srand(time(NULL));
    hdrdome_init();
    init_lang();
    s = hdrscene_read();
    aux = hdrimg_read( "bk0.pfm" );
    cd = cam_data_alloc( MAX_NFRAMES, aux->w, aux->h, .01, 1000. );
    mmove_read( cd, argv[1], argv[2] );
    mmove_view( s->view, cd, atoi( argv[3] ) );
    init_render();
    sprintf( str, "bk%i.pfm", atoi( argv[3] ) );
    s->plshadow->img = hdrimg_read( str );
    ll_y = s->view->sc.ll.y;
    ur_y = s->view->sc.ur.y;
    obj = plshadow_to_obj( s->plshadow );
    n = 0;
    c_med = C_BLACK;
    bk_med = C_BLACK;
    for(v = ll_y; v < ur_y; v += 1) {
        for (u = s->view->sc.ll.x; u < s->view->sc.ur.x; u += 1) {
            r = ray_unit(ray_transform(ray_view(u, v), mclip));
            bk = hdrimg_getc( s->plshadow->img, u, v );
            for( smpl = 0; smpl < MAX_PTRACE_SAMPLES; smpl++ ){
                if( (hit_surface( r, obj )) && (!hit_surface( r, s->objs )) &&
                    (s->plshadow != NULL) && (s->plshadow->img != NULL) &&
                    (n < MAX_N) ){
                    c_aux = dome_trace_path(PATH_SIZE, PATH_SIZE, r, obj );
                    c_med = v3_add( v3_scale( ((Real)n)/(n+1), c_med ),
                                    v3_scale( 1./(n+1), c_aux ));
                }
            }
        }
    }
}

5. 总结与展望

5.1 总结

本文详细介绍了图像基光照相关的技术和代码实现,包括局部场景渲染的差分渲染方法、处理 HDR 图像的代码模块、图像基光照的 API 和代码、HDR 场景的处理方法、穹顶路径追踪算法以及多边形阴影颜色调整程序。这些技术和代码可以帮助我们实现更真实的图像基光照效果,为计算机图形学中的光照模拟提供了有力的支持。

5.2 展望

未来,我们可以在以下几个方面对这些技术进行进一步的优化和扩展:
1. 性能优化 :目前的路径追踪算法计算量较大,性能较低。可以通过并行计算、加速结构等技术来提高算法的性能。
2. 功能扩展 :可以增加更多的光照模型和材质类型,以实现更丰富的光照效果。
3. 用户交互 :开发用户界面,让用户可以更方便地调整场景参数和光照效果。
4. 跨平台支持 :将代码移植到不同的平台上,如移动设备、游戏引擎等,以扩大应用范围。

通过不断的优化和扩展,图像基光照技术将在计算机图形学、虚拟现实、游戏开发等领域发挥更加重要的作用。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值