Пример #1
1
/*
 * Transform a direction from tangent space to object space.
 *
 * Tangent space is a right-handed coordinate system where
 * the tangent is your thumb, the normal is the index finger, and
 * the bitangent is the middle finger.
 *
 * normal, tangent, and bitangent are given in object space.
 * Build a matrix that rotates d from tangent space into object space.
 * Then, transform d with this matrix to obtain the result.
 * 
 * You may assume that normal, tangent, and bitangent are normalized
 * to length 1.
 *
 * The output vector must be normalized to length 1, even if d is not.
 */
glm::vec3 transform_direction_to_object_space(
	glm::vec3 const& d, 
	glm::vec3 const& normal, 
	glm::vec3 const& tangent, 
	glm::vec3 const& bitangent)
{
	cg_assert(std::fabs(glm::length(normal)    - 1.0f) < 1e-4f);
	cg_assert(std::fabs(glm::length(tangent)   - 1.0f) < 1e-4f);
	cg_assert(std::fabs(glm::length(bitangent) - 1.0f) < 1e-4f);
	
    glm::mat3 M(tangent, normal, bitangent);
    
    return glm::normalize(M * d);
}
Пример #2
0
void ImageTexture::
create_mipmap()
{
	/* iteratively downsample until only a 1x1 image is left */
	int size_x = mip_levels[0]->getWidth();
	int size_y = mip_levels[0]->getHeight();

	cg_assert("must be power of two" && !(size_x & (size_x - 1)));
	cg_assert("must be power of two" && !(size_y & (size_y - 1)));
	
	for (int level = 0; size_x > 1 || size_y > 1; level++)
	{
		int const cx = size_x > 1 ? 2 : 1;
		int const cy = size_y > 1 ? 2 : 1;
		size_x = std::max(1, size_x/2);
		size_y = std::max(1, size_y/2);
		mip_levels.emplace_back(new Image(size_x, size_y));
		for (int x = 0; x < size_x; x++) {
			for (int y = 0; y < size_y; y++) {
				glm::vec4 mean(0.f);
				for (int xx = 0; xx < cx; xx++) {
					for (int yy = 0; yy < cy; yy++) {
						mean += mip_levels[level]->getPixel(2*x+xx, 2*y+yy);
					}
				}
				mip_levels[level+1]->setPixel(x, y, mean/float(cx*cy));
			}
		}
	}
}
Пример #3
0
void Image::setPixel(int i, int j, const glm::vec4& pixel)
{
    cg_assert(i >= 0);
    cg_assert(j >= 0);
    cg_assert(i < m_width);
    cg_assert(j < m_height);
    m_pixels[i + j * m_width] = pixel;
}
Пример #4
0
const glm::vec4& Image::getPixel(int i, int j) const
{
    cg_assert(i >= 0);
    cg_assert(j >= 0);
    cg_assert(i < m_width);
    cg_assert(j < m_height);
    return m_pixels[i + j * m_width];
}
Пример #5
0
void ImageTexture::set_texel(int level, int x, int y, glm::vec4 const& value)
{
	cg_assert(level >= 0 && level < int(mip_levels.size()));
	cg_assert(mip_levels.at(level)->getWidth() > 0);
	cg_assert(mip_levels.at(level)->getHeight() > 0);
	cg_assert(x >= 0 && x < mip_levels.at(level)->getWidth());
	cg_assert(y >= 0 && y < mip_levels.at(level)->getHeight());
	mip_levels[level]->setPixel(x, y, value);
}
Пример #6
0
glm::vec4 ImageTexture::
evaluate_nearest(int level, glm::vec2 const& uv) const
{
	cg_assert(level >= 0 && level < static_cast<int>(mip_levels.size()));
	cg_assert(mip_levels[level]);
	int const width = mip_levels[level]->getWidth();
	int const height = mip_levels[level]->getHeight();
	int const s = (int)std::floor(uv[0]*width);
	int const t = (int)std::floor(uv[1]*height);
	return get_texel(level, s, t);
}
Пример #7
0
/*
 * Evaluates a texture for the given uv-coordinate without filtering.
 *
 * This method transformes the uv-coordinate into a st-coordinate and
 * rounds down to integer pixel values.
 *
 * The parameter level in [0, mip_levels.size()-1] is the miplevel of
 * the texture that is to be evaluated.
 */
glm::vec4 ImageTexture::
evaluate_nearest(int level, glm::vec2 const& uv) const
{
	cg_assert(level >= 0 && level < static_cast<int>(mip_levels.size()));
	cg_assert(mip_levels[level]);

	// TODO: compute the st-coordinates for the given uv-coordinates and mipmap level
	int s = floor(mip_levels[level]->getWidth() * uv[0]);
	int t = floor(mip_levels[level]->getHeight() * uv[1]);

	// get the value of pixel (s, t) of miplevel level
	return get_texel(level, s, t);
}
Пример #8
0
int HostRender::run_noninteractive(RaytracingContext& context,
                                   PixelFuncRaw const& render_pixel, int kill_timeout_seconds)
{
    Image      frame_buffer(context.params.image_width, context.params.image_height);
    ThreadPool thread_pool(context.params.num_threads);
    std::vector<glm::ivec2> tile_idx;

    Timer timer;
    timer.start();
    context.scene->refresh_scene(context.params);
    launch(&frame_buffer, thread_pool, &context, &tile_idx, render_pixel);

    if (kill_timeout_seconds > 0)
    {
        if (thread_pool.kill_at_timeout(kill_timeout_seconds))
        {
            cg_assert(!bool("Process ran into timeout - is there an infinite "
                            "loop?"));
        }
    }
    else
    {
        thread_pool.wait();
    }
    thread_pool.poll_exceptions();
    timer.stop();
    std::cout << "Rendering time: " << timer.getElapsedTimeInMilliSec() << "ms" << std::endl;
    frame_buffer.saveTGA(context.params.output_file_name.c_str(), 2.2f);

    return 0;
}
Пример #9
0
/*
 * Implement repeating here.
 *
 * The input "val" may be arbitrary, including negative and very large positive values.
 * The method shall always return a value in [0, size).
 * Out-of-bounds inputs must be mapped back into [0, size) so that 
 * the texture repeats infinitely.
 */
int ImageTexture::
wrap_repeat(int val, int size)
{
	cg_assert(size > 0);

    int temp = val % size;
	return (temp < 0) ? temp + size : temp;
}
Пример #10
0
glm::vec4 ImageTexture::
evaluate_bilinear(int level, glm::vec2 const& uv) const
{
	cg_assert(level >= 0 && level < static_cast<int>(mip_levels.size()));
	cg_assert(mip_levels[level]);
	int const width = mip_levels[level]->getWidth();
	int const height = mip_levels[level]->getHeight();
	float fs = uv[0]*width+0.5f;
	float ft = uv[1]*height+0.5f;
	float const ffs = std::floor(fs);
	float const fft = std::floor(ft);
	float const ws = fs - ffs;
	float const wt = ft - fft;

	return (1.f-ws) * (1.f-wt) * get_texel(level, ffs-1, fft-1) + 
		   (1.f-ws) * (    wt) * get_texel(level, ffs-1, fft) + 
		   (    ws) * (1.f-wt) * get_texel(level, ffs,   fft-1) + 
		   (    ws) * (    wt) * get_texel(level, ffs,   fft); 
}
std::shared_ptr<Renderer> DeviceRenderingContext::
get_current_renderer() const
{
	auto it = renderers.find(params.current_renderer);
	if(it != renderers.end())
		return it->second;

	cg_assert(!"invalid current renderer, didn't find any matching "
			"renderer in renderers");
	return nullptr;
}
Пример #12
0
/*
 * This is the main rendering kernel.
 *
 * It is supposed to compute the RGB color of the given pixel (x,y).
 *
 * RenderData contains data relevant for the computation of the color
 * for one pixel. Thread-local data is referenced by this struct, aswell. The
 * pointer tld is guaranteed to be valid (not nullptr).
 */
glm::vec3 render_pixel(int x, int y, RaytracingContext const& context, RenderData &data)
{
	cg_assert(data.tld);
	float fx = x + 0.5f;
	float fy = y + 0.5f;

	data.x = fx;
	data.y = fy;

	Ray ray = createPrimaryRay(data, fx, fy);
	return trace_recursive(data, ray, 0/*depth*/);
}
Пример #13
0
void HostRender::generate_tile_idx(int num_tiles_x, int num_tiles_y, std::vector<glm::ivec2>* tile_idx)
{
    /* Generate tile indices in the order of a spiral that starts in the center of the image.
     * This ensures that we will be able to see updates in the important region of the
     * image quickly.
     */
    int const num_tiles = num_tiles_x * num_tiles_y;

    tile_idx->resize(num_tiles);
    {
        static glm::ivec2 const dir[] = {
            glm::uvec2(1, 0),
            glm::uvec2(0, 1),
            glm::uvec2(-1, 0),
            glm::uvec2(0, -1)
        };

        glm::ivec2 current_idx(-1, 0);
        for (int i = 0, step = 0; i < num_tiles; ++step)
        {
            glm::ivec2 const d = dir[step % 4];
            int const size = (step % 2 == 0) ? num_tiles_x : num_tiles_y;
            int const num_step_tiles = size - (step+1) / 2;

            for (int j = 0; j < num_step_tiles && i < num_tiles; ++j, ++i)
            {
                cg_assert(i < num_tiles);
                current_idx += d;
                cg_assert(current_idx[0] < num_tiles_x);
                cg_assert(current_idx[1] < num_tiles_y);
                cg_assert(num_tiles-1-i >= 0);
                (*tile_idx)[num_tiles-1-i] = current_idx;
            }
        }
    }
}
Пример #14
0
/*
 * Implement clamping here.
 *
 * The input "val" may be arbitrary, including negative and very large positive values.
 * The method shall always return a value in [0, size).
 * Out-of-bounds inputs must be clamped to the nearest boundary.
 */
int ImageTexture::
wrap_clamp(int val, int size)
{
	cg_assert(size > 0);
	
    if (val < 0)
    {
        return 0;
    }
    else if (val >= size)
    {
        return size - 1;
    }

    return val;
}
Пример #15
0
glm::vec4 ImageTexture::
get_texel(int level, int x, int y) const
{
	static glm::vec4 mip_level_debug_colors[] = {
		{ 1, 0, 0, 0 },
		{ 0, 1, 0, 0 },
		{ 0, 0, 1, 0 },
		{ 1, 1, 0, 0 },
		{ 1, 0, 1, 0 },
		{ 0, 1, 1, 0 },
	};
	cg_assert(level >= 0 && level < int(mip_levels.size()));
	cg_assert(mip_levels.at(level)->getWidth() > 0);
	cg_assert(mip_levels.at(level)->getHeight() > 0);

	if(filter_mode == DEBUG_MIP) {
		int l = level % (sizeof(mip_level_debug_colors)
				/ sizeof(mip_level_debug_colors[0]));
		return mip_level_debug_colors[l];
	}


	switch (wrap_mode)
	{
		case REPEAT:
			x = wrap_repeat(x, mip_levels[level]->getWidth());
			y = wrap_repeat(y, mip_levels[level]->getHeight());
			break;

		case CLAMP:
			x = wrap_clamp(x, mip_levels[level]->getWidth());
			y = wrap_clamp(y, mip_levels[level]->getHeight());
			break;

		case ZERO:
			if (x < 0 || x >= mip_levels[level]->getWidth()
			 || y < 0 || y >= mip_levels[level]->getHeight())
			{
				return glm::vec4(0);
			}
			break;
		default:
			cg_assert(!"Invalid pixel wrap mode.");
			return glm::vec4(0);
	}

	cg_assert(x >= 0 && x < mip_levels[level]->getWidth());
	cg_assert(y >= 0 && y < mip_levels[level]->getHeight());

	return mip_levels[level]->getPixel(x, y);
}
Пример #16
0
/*
 * Intersect with the ray, but do so in object space.
 *
 * First, transform ray into object space. Use the methods you have
 * implemented for this.
 * Then, intersect the object with the transformed ray.
 * Finally, make sure you transform the intersection back into world space.
 *
 * isect is guaranteed to be a valid pointer.
 * The method shall return true if an intersection was found and false otherwise.
 *
 * isect must be filled properly if an intersection was found.
 */
bool Object::
intersect(Ray const& ray, Intersection* isect) const
{
	cg_assert(isect);

	if (RaytracingContext::get_active()->params.transform_objects) {
		// TODO: transform ray, intersect object, transform intersection
		// information back
		Ray transformedRay = transform_ray(ray, Object::transform_world_to_object);
		if (geo->intersect(transformedRay, isect))
		{
			*isect = transform_intersection(*isect, Object::transform_object_to_world, Object::transform_object_to_world_normal);
			isect->t = glm::distance(ray.origin, isect->position);
			return true;
		}
		else return false;
	}
	return geo->intersect(ray, isect);
}
Пример #17
0
int CGEndian_readf(FILE* stream, const char* format, ...)
{
    va_list args;
    const char* c = format;
    int cont = 1, items = 0;

    va_start(args, format);

    while (cont && *c) {
        void* buffer;
        int size, order, count;
        int read;

        while (isspace((unsigned char)*c))
            ++c;

        /* extract optional repetition count */
        if (isdigit((unsigned char)*c)) {
            count = 0;

            while (isdigit((unsigned char)*c)) {
                count *= 10;
                count += *c - '0';
                ++c;
            }

            while (isspace((unsigned char)*c))
                ++c;

        } else {
            count = 1;
        }

        cg_assert(*c != 0);

        /* extract field size and byte order */
        switch (*c++) {
            case 'b':
                size  = 1;
                order = CG_ENDIAN_LITTLE;
                break;

            case 'B':
                size  = 1;
                order = CG_ENDIAN_BIG;
                break;

            case 'w':
                size  = 2;
                order = CG_ENDIAN_LITTLE;
                break;
                
            case 'W':
                size  = 2;
                order = CG_ENDIAN_BIG;
                break;

            case 'd':
                size  = 4;
                order = CG_ENDIAN_LITTLE;
                break;

            case 'D':
                size  = 4;
                order = CG_ENDIAN_BIG;
                break;

            case 'q':
                size  = 8;
                order = CG_ENDIAN_LITTLE;
                break;

            case 'Q':
                size  = 8;
                order = CG_ENDIAN_BIG;
                break;

            default:
                CGError_abortFormat(
                    __FILE__, "CGEndian_readf", __LINE__,
                    "invalid format string: %s", format
                );
                return -1;
        }
        
        buffer = va_arg(args, void*);

        if (buffer) {
            items += read = fread(buffer, size, count, stream);

            if ((size > 1) && (order != HOST_ENDIANESS))
                CGEndian_swapArray(buffer, size, read);

            cont = read == count;

        } else {
            items += count;

            cont = fseek(stream, count * size, SEEK_CUR) != -1;
        }
    }

    va_end(args);

    return items;
}
Пример #18
0
int HostRender::run_interactive(RaytracingContext& context, PixelFuncRaw const& render_pixel,
                                std::function<void()> const& render_overlay)
{
    Image      frame_buffer(context.params.image_width, context.params.image_height);
    ThreadPool thread_pool(context.params.num_threads);
    std::vector<glm::ivec2> tile_idx;

    if (!GUI::init_host(context.params))
    {
        return 1;
    }

    if(context.scene)
        context.scene->set_active_camera();

    // Launch first render.
    launch(&frame_buffer, thread_pool, &context, &tile_idx, render_pixel);

    auto time_last_frame = std::chrono::high_resolution_clock::now();

    RaytracingParameters oldParams = context.params;
    while (GUI::keep_running())
    {
        GUI::poll_events();
        thread_pool.poll_exceptions();

        // Restart rendering if parameters have changed.
        auto cam = Camera::get_active();
        if ((cam && cam->requires_restart())
                || context.params.change_requires_restart(oldParams))
        {
            thread_pool.terminate();
            if (oldParams.scene != context.params.scene) {
                // reload scene
                switch (context.params.scene) {
                case RaytracingParameters::CORNELL_BOX:
                    context.scene = context.scenes["cornell_box"];
                    break;
                case RaytracingParameters::SPHERE_PORTRAIT:
                    context.scene = context.scenes["sphere_portrait"];
                    break;
                default:
                    cg_assert(!"should not happen");
                }
                cg_assert(context.scene);
                context.scene->set_active_camera();
            }
            context.scene->refresh_scene(context.params);
            oldParams = context.params;
            launch(&frame_buffer, thread_pool, &context, &tile_idx, render_pixel);
        }

        // Update the texture displayed online in regular intervals so that
        // we don't waste many cycles uploading all the time.
        auto const now = std::chrono::high_resolution_clock::now();
        float const mspf = 1000.f / static_cast<float>(context.params.fps);
        if (std::chrono::duration_cast<std::chrono::milliseconds>(now-time_last_frame).count() > mspf)
        {
            GUI::display_host(frame_buffer, render_overlay);
        }
    }

    GUI::cleanup();

    return 0;
}
DeviceRenderingContext::
~DeviceRenderingContext()
{
	cg_assert(current_context);
	current_context = nullptr;
}
Пример #20
0
glm::vec4 ImageTexture::
evaluate_bilinear(int level, glm::vec2 const& uv) const
{
	cg_assert(level >= 0 && level < static_cast<int>(mip_levels.size()));
	cg_assert(mip_levels[level]);

	const int width = mip_levels[level]->getWidth(), height = mip_levels[level]->getHeight();

	float intpart = 0.f;
	const glm::vec2 st{ uv[0] * width, uv[1] * height };

	//texel coordinates
	// (x1, y1)    (x2, y1)
	//
	// (x1, y2)    (x2, y2)



	//y coords of neighbouring texels, weight b
	float mod_t = std::modf(st[1], &intpart);
	mod_t += (mod_t < 0.f) ? 1.f : 0.f; //fractional part should be positive

	float w2 = mod_t; //weight b
	int y1 = floor(st[1]);
	int y2 = ceil(st[1]);

	if (y1 == y2) //mod_t == 0
	{
		--y1;
		w2 = 0.5f;
	}
	else if (mod_t < 0.5f)
	{
		--y1;
		--y2;
		w2 += 0.5f;
	}
    else if (mod_t == 0.5f)
    {
        --y1;
        --y2;
        w2 = 1.0f;
    }
	else //mod_t > 0.5f
		w2 -= 0.5f;
	
    //calculate x coords of neighbouring texels, weight a
	float mod_s = std::modf(st[0], &intpart);
	mod_s += (mod_s < 0.f) ? 1.f : 0.f; //fractional part should be positive

	float w1 = mod_s; //weight 1
	int x1 = floor(st[0]);
	int x2 = ceil(st[0]);
	
    if (x1 == x2) //mod_s == 0
	{
		--x1;
		w1 = 0.5f;
	}
	else if (mod_s < 0.5f)
	{
		--x1;
		--x2;
		w1 += 0.5f;
	}
    else if (mod_s == 0.5f)
    {
		--x1;
		--x2;
		w1 = 1.0f;
    }
	else //mod_s > 0.5f
		w1 -= 0.5f;


	// linear interpolations
	glm::vec4 t34 = (1.f - w1) * get_texel(level, x1, y2) + w1 * get_texel(level, x2, y2);
    glm::vec4 t12 = (1.f - w1) * get_texel(level, x1, y1) + w1 * get_texel(level, x2, y1);

	//linear interpolation vertically
	return (1.f - w2) * t12 + w2 * t34;
}
DeviceRenderingContext *DeviceRenderingContext::
get_active()
{
	cg_assert(current_context);
	return current_context;
}
DeviceRenderingContext::
DeviceRenderingContext()
{
	cg_assert(!current_context);
	current_context = this;
}
Пример #23
0
/*
 * This method creates a mipmap hierarchy for
 * the texture.
 *
 * This is done by iteratively reducing the
 * dimenison of a mipmap level and averaging
 * pixel values until the size of a mipmap
 * level is [1, 1].
 *
 * The original data of the texture is stored
 * in mip_levels[0].
 *
 * You can allocale memory for a new mipmap level 
 * with dimensions (size_x, size_y) using
 *		mip_levels.emplace_back(new Image(size_x, size_y));
 */
void ImageTexture::
create_mipmap()
{
	/* this are the dimensions of the original texture/image */
	int size_x = mip_levels[0]->getWidth();
	int size_y = mip_levels[0]->getHeight();

	cg_assert("must be power of two" && !(size_x & (size_x - 1)));
	cg_assert("must be power of two" && !(size_y & (size_y - 1)));

    //x, y: coordinates applied to new level
	int level = 0, x = 0, y = 0;     
    // smaller
	int &s = (size_x < size_y) ? x : y; 
    // bigger
	int &b = (size_x < size_y) ? y : x; 

	//loop until smaller > 0
	for (x = size_x / 2, y = size_y / 2; s > 0; x /= 2, y /= 2)
	{
		//create new level
		mip_levels.emplace_back(new Image(x, y));
		++level;

		//fill level
		for (int i = 0; i < x; ++i)
			for (int j = 0; j < y; ++j)
			{
                //coordinates applied to lower level
				int _x = i * 2, _y = j * 2; 
				
                //calculate average value of pixels on lower level and set new pixel
				glm::vec4 texel = ((mip_levels[level - 1]->getPixel(_x, _y) + mip_levels[level - 1]->getPixel(_x + 1, _y)
					+ mip_levels[level - 1]->getPixel(_x, _y + 1) + mip_levels[level - 1]->getPixel(_x + 1, _y + 1)) * 0.25f);
				
                //mip_levels[level]->setPixel(i, j, pixel);
				set_texel(level, i, j, texel);
			}
	}

	//countinue loop until bigger > 0
	
    //smaller edge has only width/heigth 1 now
    s = 1; 	
    
    for (; b > 0; b /= 2)
	{
		mip_levels.emplace_back(new Image(x, y));
		++level;
		for (int i = 0; i < b; ++i)
		{
            //coordinate applied to lower level
			int _c = i * 2; 			
            
            if (size_x >= size_y) 
			{
				//calculate average value of pixels on lower level and set new pixel
				glm::vec4 texel = ((mip_levels[level - 1]->getPixel(_c, 0) + mip_levels[level - 1]->getPixel(_c + 1, 0)) / 2.f);
                
                // help function: mip_levels[level]->setPixel(0, i, pixel);
				set_texel(level, i, 0, texel);
			}
            else //_c = _y
			{
				//calculate average value of pixels on lower level and set new pixel
				glm::vec4 texel = ((mip_levels[level - 1]->getPixel(0, _c) + mip_levels[level - 1]->getPixel(0, _c + 1)) / 2.f);
				set_texel(level, 0, i, texel);
			}
		}
	}
}