• Advertisement

ne0_kamen

Member
  • Content count

    110
  • Joined

  • Last visited

Community Reputation

371 Neutral

About ne0_kamen

  • Rank
    Member
  1. What is the best 3D game to make first?

    I second this suggestion.
  2. Chris Crawford's Dragon Speech translation

    This is slightly off topic, but for anyone who haven't seen this I strongly recommend watching the lecture. The passion Mr. Crawford puts into the presentation is mind boggling.   It would be interesting topic of conversation to discuss whether his conclusion that interactive storytelling is (was) the future is really true. From a business point of view, I think all his attempts to create such games are failures. However, I would classify a recent game such as Beyond Two Souls as Interactive story telling. Maybe not in the way Chris Crawford envisioned, but similar. Is there a whole new genre of games, which potential is not fully discovered yet?
  3.     mapxoff/mapyoff are my camera variables. They move along with my player and yes the position of the rings depend on them. If they are not in range of my camera, they don't render, heres the code in case: //update the map scroll position //camera coordinates mapxoff = player->x + player->width/2 - WIDTH/2 + 10; mapyoff = player->y + player->height/2 - HEIGHT/2 + 10; //Avoid moving beyond the map edge if(mapxoff < 0) mapxoff = 0; if(mapxoff > mapwidth*mapblockwidth - WIDTH) mapxoff = mapwidth*mapblockwidth - WIDTH; if(mapyoff < 0) mapyoff = 0; if(mapyoff > mapheight*mapblockheight - HEIGHT) mapyoff = mapheight*mapblockheight - HEIGHT;   You should have everything in the same coordinate system. For collision detection, you don't need to "scroll" the world using camera offset, just use the object's world coordinates like this :                //find the center of the player             x = player->x + player->width/2;             y = player->y + player->height/2;             //get the rings bounding rectangle             x1 = rings[n]->x ;             y1 = rings[n]->y;             x2 = x1 + rings[n]->width;             y2 = y1 + rings[n]->height;   Hope this helps, Kamen
  4. Hello, Recently, looking for new information on Half-life 3 or episode 3 I stumbled upon this article/video : [url="http://www.gamespot.com/news/half-life-3-wait-explained-6372687"]http://www.gamespot....plained-6372687[/url] [media]http://www.youtube.com/watch?feature=player_embedded&v=6VofXcw8AJQ[/media] While the article doesn't provide much information about the upcoming games of the franchise, I found the video very interesting - it summarizes very nicely what I think was great about half life 1/2 and stuff that's missing in today's fps games. The level design, the story and characters, the original weapons, the scripted events of dying scientists, the g-man encounters, the fun and replay value of the games... It just seems it isn't there in modern FPS games. Admittedly, I haven't played any of the latest shooters (my last one was Crysis 1), so I want to ask what do you think about this? Are the developers responsible for not putting enough love/work/passion into the product or is it just that today's gamers don't need games like Half-life anymore?
  5. Haiku update

    As always, great work!!
  6. Tetris in Ruby

    Hello, My exams are over (finally). One of the courses I took this semester was introduction to the Ruby language. For the final exam, we had to make some project of moderate complexity. The result was like 90% web apps (written using Rails), several console apps and several games (most of which were chess simulators, they seem to be beloved among the academia). So I figured I would do a very basic game I never did before - Tetris. Ruby is a really cool language for prototyping and its amazing how much time it could save you with its "blocks passed as arguments" and extensive Enumerable class methods. For the graphics front-end/window creation and sound, I used gosu. Its amazingly simple and fast enough for the job. It took me a few hours to program the whole thing (+unit tests). The result is not bad, but apparently not good enough (I got a 4 out of 6 score). The biggest remark I got from my lecturer was that my rendering and game logic was too tightly coupled together. Which is true, of course. However, I believe that decoupling them would complicate things quite a bit (and thus make the project bigger, andwould take me more time). I just don't get why the professors always think an over-engineered, but "extensible and maintainable" solution isbetter than a simple one. Its not like I'm going to bring my course work into a full blown product. Source require 'gosu' class Block attr_accessor :falling attr_accessor :x, :y, :width, :height, :color @@image = nil def initialize(game) # Image is loaded only once for all blocks if @@image == nil @@image = Gosu::Image.new(game, "block.png", false) end @x = 0 @y = 0 @width = @@image.width; @height = @@image.height @game = game @color = 0xffffffff end def draw @@image.draw(@x, @y, 0, 1, 1, @color) end def collide(block) # Two blocks collide only when they are at the same position, since the world is a grid return (block.x == @x && block.y == @y) end def collide_with_other_blocks @game.blocks.each do |block| if collide(block) return block end end nil end end class Shape attr_accessor :rotation def initialize(game) @game = game @last_fall_update = Gosu::milliseconds @last_move_update = Gosu::milliseconds @blocks = [Block.new(game), Block.new(game), Block.new(game), Block.new(game) ] @x = @y = 0 @falling = true # Rotation is done about this block @rotation_block = @blocks[1] # How many rotations we can do before a full cycle? @rotation_cycle = 1 # Current rotation state @rotation = 0 end def apply_rotation # Each rotation is a 90 degree in the clockwise direction if @rotation_block != nil (1..@rotation.modulo(@rotation_cycle)).each do |i| @blocks.each do |block| old_x = block.x old_y = block.y block.x = @rotation_block.x + (@rotation_block.y - old_y) block.y = @rotation_block.y - (@rotation_block.x - old_x) end end end end # Note that the following function is defined properly only when the object is unrotated # Otherwise the line of symmetry will be misplaced and wrong results will be produced def reverse # Mirror the shape by the y axis, effectively creating shape counterparts such as 'L' and 'J' center = (get_bounds[2] + get_bounds[0]) / 2.0 @blocks.each do |block| block.x = 2*center - block.x - @game.block_width end end def get_bounds # Go throug all blocks to find the bounds of this shape x_min = [] y_min = [] x_max = [] y_max = [] @blocks.each do |block| x_min @game.width ) return true end if ( bounds[0] < 0 ) return true end return false end end class ShapeI < Shape def initialize(game) super(game) @rotation_block = @blocks[1] @rotation_cycle = 2 end def get_blocks @blocks[0].x = @x @blocks[1].x = @x @blocks[2].x = @x @blocks[3].x = @x @blocks[0].y = @y @blocks[1].y = @blocks[0].y + @blocks[0].height @blocks[2].y = @blocks[1].y + @blocks[1].height @blocks[3].y = @blocks[2].y + @blocks[2].height apply_rotation @blocks.each { |block| block.color = 0xffb2ffff } end end class ShapeL < Shape def initialize(game) super(game) @rotation_block = @blocks[1] @rotation_cycle = 4 end def get_blocks @blocks[0].x = @x @blocks[1].x = @x @blocks[2].x = @x @blocks[3].x = @x + @game.block_width @blocks[0].y = @y @blocks[1].y = @blocks[0].y + @game.block_height @blocks[2].y = @blocks[1].y + @game.block_height @blocks[3].y = @blocks[2].y apply_rotation @blocks.each { |block| block.color = 0xffff7f00 } end end class ShapeJ < ShapeL def get_blocks # Reverse will reverse also the direction of rotation that's applied in apply_rotation # This will temporary disable rotation in the super method, so we can handle the rotation here after the reverse old_rotation = @rotation @rotation = 0 super reverse @rotation = old_rotation apply_rotation @blocks.each { |block| block.color = 0xff0000ff} end end class ShapeCube < Shape def get_blocks @blocks[0].x = @x @blocks[1].x = @x @blocks[2].x = @x + @game.block_width @blocks[3].x = @x + @game.block_width @blocks[0].y = @y @blocks[1].y = @blocks[0].y + @game.block_height @blocks[2].y = @blocks[0].y @blocks[3].y = @blocks[2].y + @game.block_height @blocks.each { |block| block.color = 0xffffff00} end end class ShapeZ < Shape def initialize(game) super(game) @rotation_block = @blocks[1] @rotation_cycle = 2 end def get_blocks @blocks[0].x = @x @blocks[1].x = @x + @game.block_width @blocks[2].x = @x + @game.block_width @blocks[3].x = @x + @game.block_width*2 @blocks[0].y = @y @blocks[1].y = @y @blocks[2].y = @y + @game.block_height @blocks[3].y = @y + @game.block_height apply_rotation @blocks.each { |block| block.color = 0xffff0000} end end class ShapeS < ShapeZ def get_blocks # Reverse will reverse also the direction of rotation that's applied in apply_rotation # This will temporary disable rotation in the super method, so we can handle the rotation here after the reverse old_rotation = @rotation @rotation = 0 super reverse @rotation = old_rotation apply_rotation @blocks.each { |block| block.color = 0xff00ff00} end end class ShapeT < Shape def initialize(game) super(game) @rotation_block = @blocks[1] @rotation_cycle = 4 end def get_blocks @blocks[0].x = @x @blocks[1].x = @x + @game.block_width @blocks[2].x = @x + @game.block_width*2 @blocks[3].x = @x + @game.block_width @blocks[0].y = @y @blocks[1].y = @y @blocks[2].y = @y @blocks[3].y = @y + @game.block_height apply_rotation @blocks.each { |block| block.color = 0xffff00ff} end end class TetrisGameWindow < Gosu::Window attr_accessor :blocks attr_reader :block_height, :block_width attr_reader :level attr_reader :falling_shape STATE_PLAY = 1 STATE_GAMEOVER = 2 def initialize super(320, 640, false) @block_width = 32 @block_height = 32 @blocks = [] @state = STATE_PLAY spawn_next_shape @lines_cleared = 0 @level = 0 self.caption = "Tetris : #{@lines_cleared} lines" @song = Gosu::Song.new("TetrisB_8bit.ogg") end def update if ( @state == STATE_PLAY ) if ( @falling_shape.collide ) @state = STATE_GAMEOVER else @falling_shape.update end @level = @lines_cleared / 10 self.caption = "Tetris : #{@lines_cleared} lines" else if ( button_down?(Gosu::KbSpace) ) @blocks = [] @falling_shape = nil @level = 0 @lines_cleared = 0 spawn_next_shape @state = STATE_PLAY end end if ( button_down?(Gosu::KbEscape) ) close end @song.play(true) end def draw @blocks.each { |block| block.draw } @falling_shape.draw if @state == STATE_GAMEOVER text = Gosu::Image.from_text(self, "Game Over", "Arial", 40) text.draw(width/2 - 90, height/2 - 20, 0, 1, 1) end end def button_down(id) # Rotate shape when space is pressed if ( id == Gosu::KbSpace && @falling_shape != nil ) @falling_shape.rotation += 1 if ( @falling_shape.collide ) @falling_shape.rotation -= 1 end end end def spawn_next_shape # Spawn a random shape and add the current falling shape' blocks to the "static" blocks list if (@falling_shape != nil ) @blocks += @falling_shape.get_blocks end generator = Random.new shapes = [ShapeI.new(self), ShapeL.new(self), ShapeJ.new(self), ShapeCube.new(self), ShapeZ.new(self), ShapeT.new(self), ShapeS.new(self)] shape = generator.rand(0..(shapes.length-1)) @falling_shape = shapes[shape] end def line_complete(y) # Important is that the screen resolution should be divisable by the block_width, otherwise there would be gap # If the count of blocks at a line is equal to the max possible blocks for any line - the line is complete i = @blocks.count{|item| item.y == y} if ( i == width / block_width ) return true; end return false; end def delete_lines_of( shape ) # Go through each block of the shape and check if the lines they are on are complete deleted_lines = [] shape.get_blocks.each do |block| if ( line_complete(block.y) ) deleted_lines.push(block.y) @blocks = @blocks.delete_if { |item| item.y == block.y } end end @lines_cleared += deleted_lines.length # This applies the standard gravity found in classic Tetris games - all blocks go down by the # amount of lines cleared @blocks.each do |block| i = deleted_lines.count{ |y| y > block.y } block.y += i*block_height end end end # This global prevents creation of the window and start of the simulation when we are doing testing if ( !$testing ) window = TetrisGameWindow.new window.show end And unit tests (they were required) $testing = true require "./tetris.rb" require "test/unit" class TestTetris < Test::Unit::TestCase def setup @game = TetrisGameWindow.new @w = @game.block_width @h = @game.block_height end def test_shapes_construction assert_equal(4, ShapeI.new(@game).get_blocks.length, "ShapeI must be constructed of 4 blocks") assert_equal(4, ShapeT.new(@game).get_blocks.length, "ShapeT must be constructed of 4 blocks") assert_equal(4, ShapeJ.new(@game).get_blocks.length, "ShapeJ must be constructed of 4 blocks") assert_equal(4, ShapeZ.new(@game).get_blocks.length, "ShapeZ must be constructed of 4 blocks") assert_equal(4, ShapeCube.new(@game).get_blocks.length, "ShapeO must be constructed of 4 blocks") assert_equal(4, ShapeS.new(@game).get_blocks.length, "ShapeS must be constructed of 4 blocks") assert_equal(4, ShapeL.new(@game).get_blocks.length, "ShapeL must be constructed of 4 blocks") assert_not_equal(nil, @game.falling_shape, "Falling shape shoudn't be nil") end def test_shapes_rotation shape = ShapeI.new(@game) shape.rotation = 1 assert(shape_contain_block(shape, -2*@w, @h), "Rotation of I failed!") assert(shape_contain_block(shape, -@w, @h), "Rotation of I failed!") assert(shape_contain_block(shape, 0, @h), "Rotation of I failed!") assert(shape_contain_block(shape, @w, @h), "Rotation of I failed!") shape = ShapeL.new(@game) shape.rotation = 2 assert(shape_contain_block(shape, -@w, 0), "Rotation of L failed!") assert(shape_contain_block(shape, 0, 0), "Rotation of L failed!") assert(shape_contain_block(shape, 0, @h), "Rotation of L failed!") assert(shape_contain_block(shape, 0, 2*@h), "Rotation of L failed!") shape = ShapeJ.new(@game) shape.rotation = 2 assert(shape_contain_block(shape, 2*@w, 0), "Rotation of J failed!") assert(shape_contain_block(shape, @w, 0), "Rotation of J failed!") assert(shape_contain_block(shape, @w, @h), "Rotation of J failed!") assert(shape_contain_block(shape, @w, 2*@h), "Rotation of J failed!") shape = ShapeZ.new(@game) shape.rotation = 2 assert(shape_contain_block(shape, 0, 0), "Rotation of Z failed!") assert(shape_contain_block(shape, @w, 0), "Rotation of Z failed!") assert(shape_contain_block(shape, @w, @h), "Rotation of Z failed!") assert(shape_contain_block(shape, 2*@w, @h), "Rotation of Z failed!") shape = ShapeS.new(@game) shape.rotation = 1 assert(shape_contain_block(shape, 0, -@h), "Rotation of S failed!") assert(shape_contain_block(shape, 0, 0), "Rotation of S failed!") assert(shape_contain_block(shape, @w, 0), "Rotation of S failed!") assert(shape_contain_block(shape, @w, @h), "Rotation of S failed!") shape = ShapeT.new(@game) shape.rotation = 3 assert(shape_contain_block(shape, @w, -@h), "Rotation of T failed!") assert(shape_contain_block(shape, @w, 0), "Rotation of T failed!") assert(shape_contain_block(shape, 2*@w, 0), "Rotation of T failed!") assert(shape_contain_block(shape, @w, @h), "Rotation of T failed!") end def test_block_collision block1 = Block.new(@game) block2 = Block.new(@game) block2.x = @w block2.y = 0 assert_equal(false, block1.collide(block2), "Blocks should not collide") block2.x = 0 block2.y = 0 assert_equal(true, block1.collide(block2), "Blocks should collide") end def test_line_complete (0.. (@game.width/@w - 1)).each do |i| add_block(i*@w, 0) end (0.. (@game.width/@w - 2)).each do |i| add_block(i*@w, @h) end assert_equal(true, @game.line_complete(0), "Line should be complete") assert_equal(false, @game.line_complete(@h), "Line should not be complete") shapeI = ShapeI.new(@game) @game.delete_lines_of(shapeI) (0.. (@game.width/@w - 1)).each do |i| assert_equal(false, contain_block(@game.blocks, i*@w, 0), "Line 0 should be deleted ") end end def add_block(x,y) block = Block.new(@game) block.x = x block.y = y @game.blocks If you run the game in the ruby 1.9 interpreter, be sure to install gosu : gem install gosu
  7. Realtime raytracing with OpenCL II

    I've done some research, and you are correct - every single source seems to point out that OpenCL does not support recursion. However, in my code recursion definitely works (as seen from the reflection and refraction in the images). My guess is either the latest version of OpenCL recently changed that or more likely the ATI drivers automatically emulates the recursion for you. I'm pretty sure there is performance overhead in doing so, and even more so - the recursion rays are not computed in parallel . So regardless if OpenCL support recursion or not, you probably should use non-recursive raytracing for optimum speed.
  8. Math Behind Look At Matrix 4x4

    I think I know where is the problem. First, you are "adding" and "subtracting" the target's x,y,z to the LookAtMatrix's translation vector. This is doing nothing. You should create new translation matrices with these offsets and multiply the LookAtMatrix by them(as I did in the formula). Second, the "old" transformation (Matrix4f transform(LookAtMatrix) ) is screwing you. There would be rotations and translations in this "old" matrix and they will affect what you are trying to do (I think). Now the first problem is easy to solve. The second is harder. I think the correct order is this : [CODE] // Store our old global translation Vector3 lookAtMatrixTranslation(LookAtMatrix[12],LookAtMatrix[13],LookAtMatrix[14]); // We need just the rotation part of the lookAtMatrix Matrix4f lookAtMatrixRotation(LookAtMatrix); lookAtMatrixRotation[12] = lookAtMatrixRotation[13] = lookAtMatrixRotation[14] = 0; // Here we can add the offsets directly (or use SetTranslation), because transform is the Identity matrix Matrix4f transform = Matrix4f().SetTranslation(target); // Multiply it by the old rotation - note that the old rotation does not affect the current translation transform = transform*lookAtMatrixRotation; // Not sure if you need to create new matrices here, depends on the realization of RotateAngle transform.RotateAngle(mouse_x, upVector); transform.RotateAngle(mouse_y, rightVector); // Finally translate back, but create new matrix and multiply by it // Also add the previous (global) translation transform = transform*Matrix4f().SetTranslation(-target+lookAtMatrixTranslation); LocalTranslation = Vector3(transform[12], transform[13], transform[14]); LocalRotation.SetFromMatrix(transform); [/CODE] This is very untested, so I may be missing something crucial. I'm very sure that you need to create new translation matrices and multiply by them, but not sure how to handle the old translation/rotation. Perhaps somebody else can help there
  9. Math Behind Look At Matrix 4x4

    [size=3]So you can't use the formula because you forced the order of rotation/scalation/translation multiplication? This is possible, but I think forcing the order is not a good idea (because it makes simple things complicated). You could provide a default multiplication order, but still allow for it to be changed. To answer your question : Compute the transformation as I said above. Then, set the [/size][size=3][left]LocalTranslation to the translation vector of the transformation.[/left][/size] [left][size=3][color=#282828][font=helvetica, arial, verdana, tahoma, sans-serif]Compute the quaternion as you do usually,[/font][/color][/size][/left] [left][size=3][color=#282828][font=helvetica, arial, verdana, tahoma, sans-serif]I have not tested this [/font][/color][/size][color=#282828][font=helvetica, arial, verdana, tahoma, sans-serif][size=3]and some of your math function's names are a bit vague, but its something worth trying [/size][/font][/color][img]http://public.gamedev.net//public/style_emoticons/default/smile.png[/img][/left] [left][size=3][color=#282828][font=helvetica, arial, verdana, tahoma, sans-serif]The idea is that you would still be rotating around (0,0,0), but then you will translate the objects to their proper locations.[/font][/color][/size][/left] [left][size=3][color=#282828][font=helvetica, arial, verdana, tahoma, sans-serif]Also its very important for the SetTranslation method to set the matrix's translation vector and not multiply it by a translation matrix (in order for the above to work).[/font][/color][/size][/left] [left][size=3][color=#282828][font=helvetica, arial, verdana, tahoma, sans-serif]If [/font][/color][color=#282828][font=helvetica, arial, verdana, tahoma, sans-serif]you still have problems, it would be helpful if you could tell me what you are trying and what the observable result is.[/font][/color][/size][/left]
  10. Realtime raytracing with OpenCL II

    Hello, In the first chapter of Realtime raytracing with OpenCL, we talked about how to use the OpenCL API for general computations and I wrote a program to sum 2 arrays on the GPU. Now I will explain how to write the raytracer itself. A couple of things first : I will not go into too much detail about the theory behind a raytracer. That's outside the scope of this entry - the point here is to show how to use OpenCL to accelerate raytracing. If you want to know how this stuff works,follow the excellent article by Jacco Bikker (Phantom on these forums) Also, in order to be as concise as possible, I will not explain the C++ side of things. I will post the full source at the end, so you can check it if you have any trouble undestanding how everything fits together, but if you read Part I you should be able to do that on your own. Let's begin. Data structures OpenCL allows you to create structs (as in the C language). We are going to need several of them to organize the code a bit : struct Material{ /* 0 - Standard diffuse color, 1 - Compute 'Chessboard' texture */ int computeColorType; float4 color; float reflectivity; float refractivity; }; struct Material createMaterial() { struct Material m; m.color = (float4)(1,1,1,1); m.computeColorType = 0; m.reflectivity = 0; m.refractivity = 0; return m; } struct Sphere{ struct Material* m; float3 pos; float radius; }; struct Plane{ struct Material* m; float3 normal; float3 point; }; struct Ray{ float3 origin; float3 dir; }; struct Light{ float3 pos; float3 dir; bool directional; float4 color; }; Not the most pretty way to do it, but works. You can't have constructors by the way, so I created the above "createMaterial" function that just creates new materials and fills them with defaults. struct Scene{ struct Sphere spheres[10]; int spheresCount; struct Plane planes[10]; int planesCount; struct Light lights[10]; int lightsCount; struct Material standardMaterial; }; The scene just contains all our spheres, planes and lights. The standard material is applied to geometries with no materials (where the m pointer is null). The kernel Now I'm going to skip all the mambo-jambo and jump right into the kernel function to show you how to use the data structures and setup the pipeline. Then I will explain all the peripheral methods that every raytracer has. Important note : I'm building the whole scene in the OpenCL code and not on the C++ side. This was specifically allowed by my lecturer, so I used it. If you want the raytracer to be reusable, you need to move the scene creation to the C++ program and pass it to the OpenCL program. It will probably be faster as well (if you organize it properly). __kernel void main( __global float4 *dst, uint width, uint height, __global float* viewTransform, __global float* worldTransforms ) Let me explain the kernel function's parameters dst - the output buffer to which we write our rendered image. Its of size width*height width - the width of the output buffer / resolution of rendering height - the height of the output buffer / resolution of rendering viewTransform - the camera matrix worldTransforms - an array of objects transform (could be more than one). So lets create our materials first struct Scene scene; scene.standardMaterial = createMaterial(); scene.standardMaterial.reflectivity = 0; scene.standardMaterial.computeColorType = 1; struct Material floorMaterial = createMaterial(); floorMaterial.reflectivity = 0.5; floorMaterial.computeColorType = 1; struct Material ballMaterial1 = createMaterial(); ballMaterial1.reflectivity = 1; ballMaterial1.color = (float4)(1,0,0,1); struct Material ballMaterial2 = createMaterial(); ballMaterial2.reflectivity = 1; ballMaterial2.color = (float4)(0,0,1,1); struct Material ballMaterial3 = createMaterial(); ballMaterial3.reflectivity = 1; ballMaterial3.color = (float4)(1,1,1,1); struct Material refractMaterial = createMaterial(); refractMaterial.refractivity = 1; Now fill in the geometry. Not too much to explain there. scene.spheresCount = 2; scene.spheres[0].pos = (float3)(0,0,0); scene.spheres[0].radius = 3; scene.spheres[0].m = &ballMaterial1; scene.spheres[1].pos = (float3)(0,0,-0); scene.spheres[1].radius = 3; scene.spheres[1].m = &ballMaterial2; scene.planesCount = 5; scene.planes[0].point = (float3)(0,-5,0); scene.planes[0].normal = (float3)(0,1,0); scene.planes[0].m = &floorMaterial; scene.planes[1].point = (float3)(0,40,0); scene.planes[1].normal = normalize((float3)(0,-1,0)); scene.planes[2].point = (float3)(-40,-5,0); scene.planes[2].normal = (float3)(1,1,0); scene.planes[3].point = (float3)(40,-5,0); scene.planes[3].normal = normalize((float3)(-1,1,0)); scene.planes[4].point = (float3)(0,0,0); scene.planes[4].normal = normalize((float3)(0,0,-1)); scene.planes[4].m = &refractMaterial; scene.lightsCount = 2; scene.lights[0].pos = (float3)(0,30,-20); scene.lights[0].directional = false; scene.lights[0].color = (float4)(1,1,1,1); scene.lights[1].pos = (float3)(0,30,20); scene.lights[1].dir = normalize((float3)(0,1,1)); scene.lights[1].directional = false; scene.lights[1].color = (float4)(1,1,1,1); Now, since in our demo we have 2 spheres moving we want to transform their positions by the worldTransforms scene.spheres[0].pos = matrixVectorMultiply(worldTransforms, &scene.spheres[0].pos); scene.spheres[1].pos = matrixVectorMultiply(worldTransforms+16, &scene.spheres[1].pos); If you build your scene on the c++ side out of triangles for example, you could specify all your coordinates in world coordinates, which would make this step unnecessary. Finally do the raytracing (+antialiasing) and store the result pixel color in the buffer float dx = 1.0f / (float)(width); float dy = 1.0f / (float)(height); float aspect = (float)(width) / (float)(height); dst[get_global_id(0)] = (float4)(0,0,0,0); for(int i = 0; i < kAntiAliasingSamples; i++){ for(int j = 0; j < kAntiAliasingSamples; j++){ float x = (float)(get_global_id(0) % width) / (float)(width) + dx*i/kAntiAliasingSamples; float y = (float)(get_global_id(0) / width) / (float)(height) + dy*j/kAntiAliasingSamples; x = (x -0.5f)*aspect; y = y -0.5f; struct Ray r; r.origin = matrixVectorMultiply(viewTransform, &(float3)(0, 0, -1)); r.dir = normalize(matrixVectorMultiply(viewTransform, &(float3)(x, y, 0)) - r.origin); float4 color = raytrace(&r, &scene, 0); dst[get_global_id(0)] += color / (kAntiAliasingSamples*kAntiAliasingSamples) ; } } Now we need the following perihperal functions : matrixVectorMultiply and raytrace. float3 matrixVectorMultiply(__global float* matrix, float3* vector){ float3 result; result.x = matrix[0]*((*vector).x)+matrix[4]*((*vector).y)+matrix[8]*((*vector).z)+matrix[12]; result.y = matrix[1]*((*vector).x)+matrix[5]*((*vector).y)+matrix[9]*((*vector).z)+matrix[13]; result.z = matrix[2]*((*vector).x)+matrix[6]*((*vector).y)+matrix[10]*((*vector).z)+matrix[14]; return result; } } Raytrace is the function where the actual raytracing happes, so we might want to look at it in more detail : float4 raytrace(struct Ray* ray, struct Scene* scene,int traceDepth) We accept a ray, the scene and the depth at which we currently are in recursive tracing. The following code : void* intersectObj = 0; int intersectObjType = 0; float t = intersect( ray, scene, &intersectObj, &intersectObjType); finds the first intersection of the ray in the scene and returns a pointer to the object, as well the type of this object. There is no polymorphism in OpenCL, so we need this to differentiate between the objects. Now compute the normal based on the object type and get its material. float4 color = (float4)(0,0,0,0); if ( t < kMaxRenderDist ){ float3 intersectPos = ray->origin+ray->dir*t ; float3 normal; struct Material* m = 0; if ( intersectObjType == 1 ){ normal = normalize(intersectPos-((struct Sphere*)intersectObj)->pos); m = ((struct Sphere*)intersectObj)->m; } else if (intersectObjType == 2 ){ normal = ((struct Plane*)intersectObj)->normal; m = ((struct Plane*)intersectObj)->m; } if ( !m ){ m = &scene->standardMaterial; } If there is no material we use the "standard material" Time to compute the color. I used a procedural checkboard texture for some of the planes, so we need to check the field "computeColorType". This is a good place to plug in any texturing code you might want to add. You could, for example use ""computeColorType = 2" for textured materials and supply a texture id. float4 diffuseColor = m->color; if ( m->computeColorType == 1){ if ( (int)(intersectPos.x/5.0f) % 2 == 0 ){ if ( (int)(intersectPos.z/5.0f) % 2 == 0 ){ diffuseColor = (float4)(0,0,0,0); } } else{ if ( (int)(intersectPos.z/5.0f) % 2 != 0 ){ diffuseColor = (float4)(0,0,0,0); } } } Reflection and refraction. We use raytrace recursively and increase the recursion depth : if ( traceDepth < kMaxTraceDepth && m->reflectivity > 0 ){ struct Ray reflectRay; float3 R = reflect(ray->dir, normal); reflectRay.origin = intersectPos + R*0.001; reflectRay.dir = R; diffuseColor += m->reflectivity*raytrace(&reflectRay, scene, traceDepth+1); } if ( traceDepth < kMaxTraceDepth && m->refractivity > 0 ){ struct Ray refractRay; float3 R = refract(ray->dir, normal, 0.6); if ( dot(R,normal) < 0 ){ refractRay.origin = intersectPos + R*0.001; refractRay.dir = R; diffuseColor = m->refractivity*raytrace(&refractRay, scene, traceDepth+1); } } Next add lights contribution for this ray. Note that there is some room for optimization here : We could have computed the light's contribution first (by adding pointLit*scene->lights.color*max(0.0f,dot(normal, L)) to color ). Then if color was close to black we could skip the diffuseColor computation althogether (including reflection and refraction). for(int i = 0; i < scene->lightsCount; i++){ float3 L = scene->lights.dir; float lightDist = kMaxRenderDist; if ( !scene->lights.directional ){ L = scene->lights.pos - intersectPos ; lightDist = length(L); L = normalize(L); } float pointLit = 1; struct Ray shadowRay; shadowRay.origin = intersectPos + L*0.001; shadowRay.dir = L; t = intersect( &shadowRay, scene, &intersectObj, &intersectObjType); if ( t < lightDist ){ pointLit = 0; } color += pointLit*diffuseColor*scene->lights.color*max(0.0f,dot(normal, L)); } } return clamp(color,0,1); We also shoot the shadow rays here. It might be a good idea to add some indication that this is a shadow ray to the intersect routine, because we might make additional optimization : we don't need to find the closest intersetction, but the first intersection that's closer than the light (if there is one). Finally we return the color and clamp each component between [0,1] Now we need 3 more functions : reflect, refract and intersect. float3 reflect(float3 V, float3 N){ return V - 2.0f * dot( V, N ) * N; } float3 refract(float3 V, float3 N, float refrIndex) { float cosI = -dot( N, V ); float cosT2 = 1.0f - refrIndex * refrIndex * (1.0f - cosI * cosI); return (refrIndex * V) + (refrIndex * cosI - sqrt( cosT2 )) * N; } Intersection is pretty straightforward. We look for the closest intersection and save the object and its type. float intersect(struct Ray* ray, struct Scene* scene, void** object, int* type) { float minT = kMaxRenderDist; for(int i = 0; i < scene->spheresCount; i++){ float t; if ( raySphere( &scene->spheres, ray, &t ) ){ if ( t < minT ){ minT = t; *type = 1; *object = &scene->spheres; } } } for(int i = 0; i < scene->planesCount; i++){ float t; if ( rayPlane( &scene->planes, ray, &t ) ){ if ( t < minT ){ minT = t; *type = 2; *object = &scene->planes; } } } return minT; } Finally, the functions to intersect ray with plane and sphere : bool raySphere(struct Sphere* s, struct Ray* r, float* t) { float3 rayToCenter = s->pos - r->origin ; float dotProduct = dot(r->dir,rayToCenter); float d = dotProduct*dotProduct - dot(rayToCenter,rayToCenter)+s->radius*s->radius; if ( d < 0) return false; *t = (dotProduct - sqrt(d) ); if ( *t < 0 ){ *t = (dotProduct + sqrt(d) ) ; if ( *t < 0){ return false; } } return true; } bool rayPlane(struct Plane* p, struct Ray* r, float* t) { float dotProduct = dot(r->dir,p->normal); if ( dotProduct == 0){ return false; } *t = dot(p->normal,p->point-r->origin) / dotProduct ; return *t >= 0; } The full source of the OpenCL program const int kAntiAliasingSamples = 2; const int kMaxTraceDepth = 2; const float kMaxRenderDist = 1000.0f; struct Material{ /* 0 - Standard diffuse color, 1 - Compute 'Chessboard' texture */ int computeColorType; float4 color; float reflectivity; float refractivity; }; struct Material createMaterial() { struct Material m; m.color = (float4)(1,1,1,1); m.computeColorType = 0; m.reflectivity = 0; m.refractivity = 0; return m; } struct Sphere{ struct Material* m; float3 pos; float radius; }; struct Plane{ struct Material* m; float3 normal; float3 point; }; struct Ray{ float3 origin; float3 dir; }; struct Light{ float3 pos; float3 dir; bool directional; float4 color; }; struct Scene{ struct Sphere spheres[10]; int spheresCount; struct Plane planes[10]; int planesCount; struct Light lights[10]; int lightsCount; struct Material standardMaterial; }; float3 reflect(float3 V, float3 N){ return V - 2.0f * dot( V, N ) * N; } float3 refract(float3 V, float3 N, float refrIndex) { float cosI = -dot( N, V ); float cosT2 = 1.0f - refrIndex * refrIndex * (1.0f - cosI * cosI); return (refrIndex * V) + (refrIndex * cosI - sqrt( cosT2 )) * N; } bool raySphere(struct Sphere* s, struct Ray* r, float* t) { float3 rayToCenter = s->pos - r->origin ; float dotProduct = dot(r->dir,rayToCenter); float d = dotProduct*dotProduct - dot(rayToCenter,rayToCenter)+s->radius*s->radius; if ( d < 0) return false; *t = (dotProduct - sqrt(d) ); if ( *t < 0 ){ *t = (dotProduct + sqrt(d) ) ; if ( *t < 0){ return false; } } return true; } bool rayPlane(struct Plane* p, struct Ray* r, float* t) { float dotProduct = dot(r->dir,p->normal); if ( dotProduct == 0){ return false; } *t = dot(p->normal,p->point-r->origin) / dotProduct ; return *t >= 0; } float intersect(struct Ray* ray, struct Scene* scene, void** object, int* type) { float minT = kMaxRenderDist; for(int i = 0; i < scene->spheresCount; i++){ float t; if ( raySphere( &scene->spheres, ray, &t ) ){ if ( t < minT ){ minT = t; *type = 1; *object = &scene->spheres; } } } for(int i = 0; i < scene->planesCount; i++){ float t; if ( rayPlane( &scene->planes, ray, &t ) ){ if ( t < minT ){ minT = t; *type = 2; *object = &scene->planes; } } } return minT; } float4 raytrace(struct Ray* ray, struct Scene* scene,int traceDepth) { void* intersectObj = 0; int intersectObjType = 0; float t = intersect( ray, scene, &intersectObj, &intersectObjType); float4 color = (float4)(0,0,0,0); if ( t < kMaxRenderDist ){ float3 intersectPos = ray->origin+ray->dir*t ; float3 normal; struct Material* m = 0; if ( intersectObjType == 1 ){ normal = normalize(intersectPos-((struct Sphere*)intersectObj)->pos); m = ((struct Sphere*)intersectObj)->m; } else if (intersectObjType == 2 ){ normal = ((struct Plane*)intersectObj)->normal; m = ((struct Plane*)intersectObj)->m; } if ( !m ){ m = &scene->standardMaterial; } float4 diffuseColor = m->color; if ( m->computeColorType == 1){ if ( (int)(intersectPos.x/5.0f) % 2 == 0 ){ if ( (int)(intersectPos.z/5.0f) % 2 == 0 ){ diffuseColor = (float4)(0,0,0,0); } } else{ if ( (int)(intersectPos.z/5.0f) % 2 != 0 ){ diffuseColor = (float4)(0,0,0,0); } } } if ( traceDepth < kMaxTraceDepth && m->reflectivity > 0 ){ struct Ray reflectRay; float3 R = reflect(ray->dir, normal); reflectRay.origin = intersectPos + R*0.001; reflectRay.dir = R; diffuseColor += m->reflectivity*raytrace(&reflectRay, scene, traceDepth+1); } if ( traceDepth < kMaxTraceDepth && m->refractivity > 0 ){ struct Ray refractRay; float3 R = refract(ray->dir, normal, 0.6); if ( dot(R,normal) < 0 ){ refractRay.origin = intersectPos + R*0.001; refractRay.dir = R; diffuseColor = m->refractivity*raytrace(&refractRay, scene, traceDepth+1); } } for(int i = 0; i < scene->lightsCount; i++){ float3 L = scene->lights.dir; float lightDist = kMaxRenderDist; if ( !scene->lights.directional ){ L = scene->lights.pos - intersectPos ; lightDist = length(L); L = normalize(L); } float pointLit = 1; struct Ray shadowRay; shadowRay.origin = intersectPos + L*0.001; shadowRay.dir = L; t = intersect( &shadowRay, scene, &intersectObj, &intersectObjType); if ( t < lightDist ){ pointLit = 0; } color += pointLit*diffuseColor*scene->lights.color*max(0.0f,dot(normal, L)); } } return clamp(color,0,1); } float3 matrixVectorMultiply(__global float* matrix, float3* vector){ float3 result; result.x = matrix[0]*((*vector).x)+matrix[4]*((*vector).y)+matrix[8]*((*vector).z)+matrix[12]; result.y = matrix[1]*((*vector).x)+matrix[5]*((*vector).y)+matrix[9]*((*vector).z)+matrix[13]; result.z = matrix[2]*((*vector).x)+matrix[6]*((*vector).y)+matrix[10]*((*vector).z)+matrix[14]; return result; } __kernel void main( __global float4 *dst, uint width, uint height, __global float* viewTransform, __global float* worldTransforms ) { struct Scene scene; scene.standardMaterial = createMaterial(); scene.standardMaterial.reflectivity = 0; scene.standardMaterial.computeColorType = 1; struct Material floorMaterial = createMaterial(); floorMaterial.reflectivity = 0.5; floorMaterial.computeColorType = 1; struct Material ballMaterial1 = createMaterial(); ballMaterial1.reflectivity = 1; ballMaterial1.color = (float4)(1,0,0,1); struct Material ballMaterial2 = createMaterial(); ballMaterial2.reflectivity = 1; ballMaterial2.color = (float4)(0,0,1,1); struct Material ballMaterial3 = createMaterial(); ballMaterial3.reflectivity = 1; ballMaterial3.color = (float4)(1,1,1,1); struct Material refractMaterial = createMaterial(); refractMaterial.refractivity = 1; scene.spheresCount = 2; scene.spheres[0].pos = (float3)(0,0,0); scene.spheres[0].radius = 3; scene.spheres[0].m = &ballMaterial1; scene.spheres[1].pos = (float3)(0,0,-0); scene.spheres[1].radius = 3; scene.spheres[1].m = &ballMaterial2; scene.planesCount = 5; scene.planes[0].point = (float3)(0,-5,0); scene.planes[0].normal = (float3)(0,1,0); scene.planes[0].m = &floorMaterial; scene.planes[1].point = (float3)(0,40,0); scene.planes[1].normal = normalize((float3)(0,-1,0)); scene.planes[2].point = (float3)(-40,-5,0); scene.planes[2].normal = (float3)(1,1,0); scene.planes[3].point = (float3)(40,-5,0); scene.planes[3].normal = normalize((float3)(-1,1,0)); scene.planes[4].point = (float3)(0,0,0); scene.planes[4].normal = normalize((float3)(0,0,-1)); scene.planes[4].m = &refractMaterial; scene.lightsCount = 2; scene.lights[0].pos = (float3)(0,30,-20); scene.lights[0].directional = false; scene.lights[0].color = (float4)(1,1,1,1); scene.lights[1].pos = (float3)(0,30,20); scene.lights[1].dir = normalize((float3)(0,1,1)); scene.lights[1].directional = false; scene.lights[1].color = (float4)(1,1,1,1); scene.spheres[0].pos = matrixVectorMultiply(worldTransforms, &scene.spheres[0].pos); scene.spheres[1].pos = matrixVectorMultiply(worldTransforms+16, &scene.spheres[1].pos); float dx = 1.0f / (float)(width); float dy = 1.0f / (float)(height); float aspect = (float)(width) / (float)(height); dst[get_global_id(0)] = (float4)(0,0,0,0); for(int i = 0; i < kAntiAliasingSamples; i++){ for(int j = 0; j < kAntiAliasingSamples; j++){ float x = (float)(get_global_id(0) % width) / (float)(width) + dx*i/kAntiAliasingSamples; float y = (float)(get_global_id(0) / width) / (float)(height) + dy*j/kAntiAliasingSamples; x = (x -0.5f)*aspect; y = y -0.5f; struct Ray r; r.origin = matrixVectorMultiply(viewTransform, &(float3)(0, 0, -1)); r.dir = normalize(matrixVectorMultiply(viewTransform, &(float3)(x, y, 0)) - r.origin); float4 color = raytrace(&r, &scene, 0); dst[get_global_id(0)] += color / (kAntiAliasingSamples*kAntiAliasingSamples) ; } } } The full source of the C++ program #include #include #include #include #include #include const int kWidth = 1366; const int kHeight = 768; const bool kFullscreen = true; size_t global_work_size = kWidth * kHeight; float viewMatrix[16]; float sphere1Pos[3] = {0,0,10}; float sphere2Pos[3] = {0,0,-10}; float sphereVelocity = 1; float sphereTransforms[2][16]; cl_command_queue queue; cl_kernel kernel; cl_mem buffer, viewTransform, worldTransforms; void InitOpenCL() { // 1. Get a platform. cl_platform_id platform; clGetPlatformIDs( 1, &platform, NULL ); // 2. Find a gpu device. cl_device_id device; clGetDeviceIDs( platform, CL_DEVICE_TYPE_GPU, 1, &device, NULL); // 3. Create a context and command queue on that device. cl_context context = clCreateContext( NULL, 1, &device, NULL, NULL, NULL); queue = clCreateCommandQueue( context, device, 0, NULL ); // 4. Perform runtime source compilation, and obtain kernel entry point. std::ifstream file("kernel.txt"); std::string source; while(!file.eof()){ char line[256]; file.getline(line,255); source += line; } cl_ulong maxSize; clGetDeviceInfo(device, CL_DEVICE_MAX_MEM_ALLOC_SIZE , sizeof(cl_ulong), &maxSize, 0); const char* str = source.c_str(); cl_program program = clCreateProgramWithSource( context, 1, &str, NULL, NULL ); cl_int result = clBuildProgram( program, 1, &device, NULL, NULL, NULL ); if ( result ){ std::cout
  11. Math Behind Look At Matrix 4x4

    There is nothing wrong in composing the LookAtMatrix directly - all you are doing is saving the matrix multiplication. However, using the LookAtMatrix, as you may have found out, is not always as practical as just composing the matrix out of rotation and translation. In your particular case, OrbitAroundTarget could be implemented as easily with either I guess. Use the following transformation to rotate around any point : // Rotate around the target point Matrix transform = Matrix().translate(target)*Matrix().rotate(angle, rotationVector)* Matrix().translate(-target); In my opinion a good generic camera class should contain just the camera transformation (and support methods for LookAt and translation/rotation) and allow you to set any matrix as the camera's transform. You can then inherit from this class and make e.g FpsCamera, OrbitAroundTargetCamera... which handle all the additional stuff. Sorry If I'm stating something you already know
  12. Math Behind Look At Matrix 4x4

    [quote name='Neosettler' timestamp='1329277378' post='4913224'] Thank you for your response ne0, [CODE]It has to do with the order of multiplication of the rotation and translation matrix that's used by the LookAt matrix.[/CODE] While that make sense, I’m not out of the woods yet. If it is not too much to ask, I’m wondering why you`ve mentioned one matrix for rotation and one for translation? I was under the impression the look at matrix combines rotation and translation all together and all you need is the eye, center and the up vector. [/quote] Exactly, the LookAt matrix combines rotation and translation - hence it could be decomposed into a rotation matrix and translation matrix which when multiplied together yield the LookAt matrix. Since you were wondering why the last column of the LookAt matrix takes dot products and not just eye's x, y, z - the reason is exactly because the whole LookAt matrix is composed in one step, so you don't see the actual multiplication of the rotation and translation matrices. I think what you might be forgetting is that this matrix is not applied to the camera - but the objects themsleves. So imagine you want to rotate 90 degrees to the left : [attachment=7243:Untitled.png] So imagine now you want to also move the camera's eye position by 1 unit backwards. Would it make any sense to move the object on z axis? No - you need to translate it one unit on the x axis - which is where the dot products came into place. I hope that makes any sense, because I feel I'm explaining it very poorly. Someone else (like jyk) would be able to explain it better perhaps...
  13. Math Behind Look At Matrix 4x4

    It has to do with the order of multiplication of the rotation and translation matrix that's used by the LookAt matrix. So if you have rotation = [ leftVector, 0 ] [ upVector, 0 ] [ fVector, 0 ] [ 0 , 0, 0, 1 ] translation = [ 0 0 0 ] [ 0 0 0 translationVector ] [ 0 0 0 ] [ 0 0 0 1 ] and your order of multiplication is rotation*translation, then the result for the last column in the lookat matrix will have the dot products. Its important to keep this order of multiplication for the lookAt matrix to have the desired effect - because you want to specify your eye position in "global coordinates" If you reverse the order - the translation will be affected by the rotation.
  14. Sigh...

    At least your visual styles are vastly different It could have been worse... I really hope you continue working though, your game seems to have a unique touch to it.
  15. Realtime raytracing with OpenCL I

    Hello, As my university's exams are going, I had been busy working on various course projects. This semester I took a Raytracing course. That's something I've been playing with some time ago, and I thought I could get some credits without paying much attention (which is important due to my lack of time and motivation to go to lectures ). Anyway, my course task is to "Use OpenCL to write a real-time raytracer" - which was very fun in my opinion. Since that's something I've never done before and I believe some folk over here would find it interesting, I decided to write 2 short journal entries of my experience. Introduction to OpenCL OpenCL( Open Computing Language) is a hardware - independent language for performing computations in massively parallel manner. In general you could run it on anything that has drivers for it - GPU or CPU. The language itself is very similar to C (not C++) with a few additions and exceptions. It also provides a small standard library of functions (mainly math functions) and native types for tuples (float3/float4). In this journal, I will write and explain a small demo of how to use OpenCL for general computations. The actual raytracer will be expained in the next journal. Compiling and linking OpenCL applications First you need to decide on what hardware you will run your app. Since I have an ATI 5650 GPU, I downloaded the AMD APP SDK from http://developer.amd...es/default.aspx This (by default) installed the OpenCL.lib file n C:\Program Files\AMD APP\lib\x86 and all headers in C:\Program Files\AMD APP\include\CL . Write the following application and link against the OpenCL.lib #include void main() { // 1. Get a platform. cl_platform_id platform; clGetPlatformIDs( 1, &platform, NULL ); } If it builds and links proceed to the next step : Building a minimal application So now that you can use the OpenCL API, its time to write a short OpenCL app. We need 2 things : First to write an "OpenCL kernel" - this is a small function written in the OpenCL language that is executed in a separate thread, and second - to init the OpenCL library properly and pass data to the "kernel" - the data that our GPU will be processing in this case. If you are intrested in detailed description of a OpenCL API function, visit http://www.khronos.o...ocs/man/xhtml/. I'm gonna keep this as brief as possible, but mostly this is boilerplate code you probably would want to copy/paste every time. As you already saw, we need to get an OpenCL platform : // 1. Get a platform. cl_platform_id platform; clGetPlatformIDs( 1, &platform, NULL ); The first argument sets how many platforms we want to get (if the second param is an array) and we could use the last parameter to get the maximum number of platforms available. After that we need to request a "device". A device is the actual hardware we are going to use - in our case our GPU : // 2. Find a gpu device. cl_device_id device; clGetDeviceIDs( platform, CL_DEVICE_TYPE_GPU, 1, &device, NULL); We pass our platform as the 1st parameter and the type of the device as the 2nd. Now we need a context and a command queue. // 3. Create a context and command queue on that device. cl_context context = clCreateContext( NULL, 1, &device, NULL, NULL, NULL); cl_command_queue queue = clCreateCommandQueue( context, device, 0, NULL ); If you are intrested in the parameters, look at he specification. Now we need to create the actual OpenCL program. Its advisable to store it in a file and load it from there. Let's call this file kernel.txt. Here is how we are gonna read its contents and load it : // 4. Perform runtime source compilation, and obtain kernel entry point. std::ifstream file("kernel.txt"); std::string source; while(!file.eof()){ char line[256]; file.getline(line,255); source += line; } const char* str = source.c_str(); cl_program program = clCreateProgramWithSource( context, 1, &str, NULL, NULL ); cl_int result = clBuildProgram( program, 1, &device, NULL, NULL, NULL ); if ( result ){ std::cout
  • Advertisement