1 #include "RenderThread.h" 2 3 #include "SyntopiaCore/Math/Vector3.h" 4 5 using namespace SyntopiaCore::Math; 6 7 namespace SyntopiaCore { 8 namespace GLEngine { 9 10 RenderThread()11 RenderThread::RenderThread() { 12 backgroundColor = Vector3f(0,0,0); 13 aoSamples = 1; 14 aaSamples = 8; 15 useShadows = true; 16 copy = false; 17 lightPos = Vector3f(0,0,0); 18 19 dofCenter = 0; 20 dofFalloff = 0; 21 sampler = 0; 22 terminated = false; 23 //filter = new GaussianFilter(0.75,1); 24 //filter = new TriangleFilter(1); 25 filter = new BoxFilter(); 26 maxDepth = 2; 27 } 28 29 ~RenderThread()30 RenderThread::~RenderThread() { 31 delete accelerator; 32 delete sampler; 33 if (!copy) delete (filter); 34 } 35 36 RenderThread(const RenderThread & other)37 RenderThread::RenderThread(const RenderThread& other) : QThread() { 38 rayIDs = other.rayIDs; 39 terminated = false; 40 41 frontStart = other.frontStart; 42 frontX = other.frontX; 43 frontY = other.frontY; 44 backStart = other.backStart; 45 backX = other.backX; 46 backY = other.backY; 47 48 lightPos = other.lightPos; 49 backgroundColor = other.backgroundColor; 50 accelerator = new VoxelStepper(*other.accelerator); 51 accelerator->setCopy(true); 52 53 aoSamples = other.aoSamples; 54 maxDepth = other.maxDepth; 55 width = other.width; 56 height = other.height; 57 useShadows = other.useShadows; 58 w = other.w; 59 h = other.h; 60 61 task = other.task; 62 nextUnit = other.nextUnit; 63 completedUnits = other.completedUnits; 64 maxUnits = other.maxUnits; 65 66 aaSamples = other.aaSamples; 67 68 dofCenter = other.dofCenter; 69 dofFalloff = other.dofFalloff; 70 71 copy = true; 72 73 rayID = 0; 74 pixels = 0; 75 checks = 0; 76 rg.randomizeUniformCounter(); // to avoid coherence between threads 77 sampler = other.sampler->clone(&rg); 78 progressiveOutput = other.progressiveOutput; 79 filter = other.filter; 80 }; 81 82 83 getAOStrength(Object3D * object,Vector3f objectNormal,Vector3f objectIntersection)84 double RenderThread::getAOStrength(Object3D* object, Vector3f objectNormal, Vector3f objectIntersection) { 85 86 if (aoSamples == 0 || object==0) return 1.0; 87 double tests = 0; 88 double hits = 0; 89 for (int ix = 0; ix < aoSamples*aoSamples; ix++) { 90 91 Vector3f random = sampler->getAODirection(rayNumber*aoSamples*aoSamples+ix); 92 if (Vector3f::dot(random, objectNormal)<0) random = random*-1.0; // Only check away from surface. 93 random.normalize(); 94 95 double maxT = 0; 96 QList<Object3D*>* list = accelerator->setupRay(objectIntersection,random, maxT); 97 RayInfo ri; 98 ri.startPoint = objectIntersection; 99 ri.lineDirection = random; 100 bool occluded = false; 101 while (list != 0 && !occluded) { 102 // check objects 103 for (int i = 0; i < list->size(); i++) { 104 if (list->at(i) == object) continue; // self-shadow? 105 occluded = list->at(i)->intersectsRay(&ri); 106 if (ri.intersection < 1E-5) occluded = false; 107 if (occluded) break; 108 } 109 if (!occluded) list = accelerator->advance(maxT); 110 } 111 double weight = 1.0; // Vector3f::dot(random, objectNormal); 112 if (occluded) hits+=weight; 113 tests += weight; 114 } 115 return 1-hits/tests; 116 } 117 raytrace(int newUnit)118 void RenderThread::raytrace(int newUnit) { 119 int x = newUnit-1; 120 float fx = x/(float)(w); 121 float xs = (1.0/w); 122 float ys = (1.0/h); 123 Vector3f* colors = new Vector3f[h]; 124 125 for (int y = 0; y < h; y++) { 126 float weightSum = 0.0; 127 sampler->prepareSamples(aaSamples,aoSamples); 128 float fy = y/(float)(h); 129 Vector3f color(0,0,0); 130 for (int i = 0; i < aaSamples*aaSamples; i++) { 131 rayNumber = i; 132 Vector3f ls = sampler->getAASample(rayNumber); 133 float weight = 1.0; 134 weightSum += weight; 135 color = color + weight*rayCastPixel(fx+ls.x()*xs,fy+ls.y()*ys); 136 } 137 colors[y] = color / weightSum; 138 } 139 140 progressiveOutput->addColumn(x,colors); 141 delete[] colors; 142 143 }; 144 raytraceProgressive(int newUnit)145 void RenderThread::raytraceProgressive(int newUnit) { 146 // rayNumber = (newUnit-1); 147 RandomNumberGenerator rg; 148 rg.setSeed(0); 149 150 double* weights = new double[w*h]; 151 Vector3f* colors = new Vector3f[w*h]; 152 for (int i = 0; i < w*h; i++) { weights[i] = 0; colors[i] = Vector3f(0,0,0); } 153 float xs = (1.0/w); 154 float ys = (1.0/h); 155 sampler->prepareSamples(aaSamples,aoSamples); 156 int extent = filter->getExtent(); 157 158 for (int y = 0; y < h; y++) { 159 160 int yFrom = y - extent; if (yFrom<0) yFrom=0; 161 int yTo = y + extent; if (yTo>=h) yTo=h-1; 162 163 float fy = y*ys; 164 if (terminated) break; 165 for (int x = 0; x < w; x++) { 166 rayNumber = ((newUnit-1) + rg.getInt(maxUnits)) % maxUnits; 167 168 int xFrom = x - extent; if (xFrom<0) xFrom=0; 169 int xTo = x + extent; if (xTo>=w) xTo=w-1; 170 171 float fx = x*xs; 172 Vector3f ls = sampler->getAASample(rayNumber); 173 Vector3f sample = rayCastPixel(fx+ls.x()*xs,fy+ls.y()*ys); 174 175 for (int xf = xFrom; xf<= xTo; xf++) { 176 for (int yf = yFrom; yf<= yTo; yf++) { 177 float dx = (xf - x)+ls.x(); 178 float dy = (yf - y)+ls.y(); 179 float wi = filter->getWeight(dx*dx,dy*dy); 180 colors[xf+yf*w] = colors[xf+yf*w] + wi*sample; 181 weights[xf+yf*w] += wi; 182 } 183 } 184 } 185 } 186 187 progressiveOutput->addIteration(colors, weights); 188 delete[] weights; 189 delete[] colors; 190 }; 191 run()192 void RenderThread::run() { 193 int newUnit = nextUnit->increase(); 194 while (newUnit <= maxUnits) { 195 // do work here... 196 switch (task) { 197 case Raytrace: raytrace(newUnit); break; 198 case RaytraceProgressive: raytraceProgressive(newUnit); break; 199 default: throw(1); 200 } 201 completedUnits->increase(); 202 newUnit = nextUnit->increase(); 203 } 204 } 205 206 setCounters(AtomicCounter * nextUnit,AtomicCounter * completedUnits,int maxUnits)207 void RenderThread::setCounters(AtomicCounter* nextUnit, AtomicCounter* completedUnits, int maxUnits) 208 { 209 this->nextUnit = nextUnit; 210 this->completedUnits = completedUnits; 211 this->maxUnits = maxUnits; 212 } 213 alloc(int w,int h)214 void RenderThread::alloc(int w, int h) { 215 this->w = w; 216 this->h = h; 217 rayID = 0; 218 pixels = 0; 219 checks = 0; 220 } 221 rayCastPixel(float x,float y)222 Vector3f RenderThread::rayCastPixel(float x, float y) { 223 224 Vector3f startPoint = frontStart + frontX*x + frontY*y; 225 Vector3f endPoint = backStart + backX*x + backY*y; 226 227 if (dofCenter == 0) { 228 Vector3f direction = endPoint - startPoint; 229 return rayCast(startPoint, direction, 0); 230 } else { 231 Vector3f centerPoint =(endPoint-startPoint)* dofCenter+ startPoint; 232 // --- Uniform Disc Sampling 233 Vector3f displace = sampler->getLensSample(rayNumber)*dofFalloff; 234 Vector3f newStartPoint = frontStart + frontX*(x+displace.x())+ frontY*(y+displace.y()); 235 Vector3f direction = (centerPoint - newStartPoint)*(1/dofCenter); 236 return rayCast(newStartPoint, direction, 0); 237 } 238 } 239 rayCast(Vector3f startPoint,Vector3f direction,Object3D * excludeThis,int level)240 Vector3f RenderThread::rayCast(Vector3f startPoint, Vector3f direction, Object3D* excludeThis, int level) { 241 if (level>maxDepth) return Vector3f(backgroundColor.x(),backgroundColor.y(),backgroundColor.z()); 242 rayID++; 243 pixels++; 244 245 double lengthToClosest = -1; 246 Vector3f foundNormal; 247 GLfloat foundColor[4]; 248 for (int i = 0; i < 4; i++) foundColor[i] = 0; 249 250 Object3D* bestObj = 0; 251 double maxT = 0; 252 RayInfo ri; 253 QList<Object3D*>* list = accelerator->setupRay(startPoint, direction, maxT); 254 ri.startPoint = startPoint; 255 ri.lineDirection = direction; 256 257 while (list != 0) { 258 // check objects 259 for (int i = 0; i < list->count(); i++) { 260 checks++; 261 262 if (list->at(i) == excludeThis) continue; 263 // Check if we already tested this... 264 if (rayIDs[list->at(i)->getObjectID()] == rayID) continue; 265 266 bool found = list->at(i)->intersectsRay(&ri); 267 rayIDs[list->at(i)->getObjectID()]= rayID; 268 if (!found) continue; 269 if ((ri.intersection<1E-7)) continue; 270 271 if ((ri.intersection>0) && ((ri.intersection <= lengthToClosest) || (lengthToClosest == -1))) { 272 // We hit something and it was closer to us than the object before... 273 foundNormal = ri.normal; 274 for (int ix = 0; ix < 4; ix++) foundColor[ix] = ri.color[ix]; 275 lengthToClosest = ri.intersection; 276 bestObj = list->at(i); 277 } 278 } 279 280 if (bestObj != 0 && lengthToClosest < maxT) break; 281 list = accelerator->advance(maxT); 282 } 283 // Now we can calculate the lightning. 284 if (lengthToClosest>0) { 285 // iPoint is the intersection point in 3D. 286 Vector3f iPoint = startPoint + direction*lengthToClosest; 287 Vector3f lightDirection = (lightPos-iPoint); 288 double light = 0; 289 290 // This is a Phong lightning model, see e.g. (http://ai.autonomy.net.au/wiki/Graphics/Illumination) 291 // -- Diffuse light 292 double diffuse = bestObj->getPrimitiveClass()->diffuse*(Vector3f::dot(foundNormal, (lightDirection).normalized())); 293 if (diffuse<0) diffuse = 0; 294 light += diffuse; 295 296 // -- Specular light 297 double spec = 0; 298 double dot = Vector3f::dot(foundNormal, lightDirection); 299 if (dot<0.1) { 300 } else { 301 Vector3f reflected = foundNormal*dot*2 - lightDirection; 302 reflected.normalize(); 303 spec = -(Vector3f::dot(reflected, (direction).normalized())); 304 if (spec< 0.1) { 305 spec = 0; 306 } else { 307 spec = bestObj->getPrimitiveClass()->specular*pow(spec,50); 308 if (spec<0) spec = 0; 309 } 310 } 311 light += spec; 312 313 // -- Ambient light 314 double aoStrength = 1.0; 315 // We will only check for AO at first intersection... 316 if (level == 0) aoStrength = getAOStrength(bestObj, foundNormal, iPoint); 317 318 double ambient = bestObj->getPrimitiveClass()->ambient*aoStrength; 319 light += ambient; 320 321 // -- calculate shadow... 322 // TODO: Calculate shadow in transperant media 323 bool inShadow = false; 324 if (useShadows) { 325 double maxT = 0; 326 QList<Object3D*>* list = accelerator->setupRay(iPoint,(lightPos-iPoint), maxT); 327 ri.startPoint = iPoint; 328 ri.lineDirection = lightPos-iPoint; 329 330 while (list != 0 && !inShadow) { 331 // check objects 332 for (int i = 0; i < list->size(); i++) { 333 if (list->at(i) == bestObj) continue; // self-shadow? (probably not neccesary, since the specular light will be negative) 334 inShadow = list->at(i)->intersectsRay(&ri); 335 if (ri.intersection < 1E-5 || ri.intersection > 1) inShadow = false; 336 if (ri.color[3]<1) inShadow=false; 337 if (inShadow) break; 338 } 339 340 if (!inShadow) list = accelerator->advance(maxT); 341 } 342 } 343 344 if (useShadows && inShadow) light=ambient; // drop-shadow strength (only ambient light...) 345 if (light < 0) light = 0; 346 347 foundColor[0] = foundColor[0]*light; 348 foundColor[1] = foundColor[1]*light; 349 foundColor[2] = foundColor[2]*light; 350 351 if (foundColor[3] < 1) { 352 Vector3f color = rayCast(iPoint, direction, bestObj, level+1); 353 foundColor[0] = foundColor[0]*(foundColor[3]) + color.x()*(1-foundColor[3]); 354 foundColor[1] = foundColor[1]*(foundColor[3]) + color.y()*(1-foundColor[3]); 355 foundColor[2] = foundColor[2]*(foundColor[3]) + color.z()*(1-foundColor[3]); 356 } 357 358 double reflection = bestObj->getPrimitiveClass()->reflection; 359 360 if (reflection > 0) { 361 Vector3f nDir = foundNormal*(-2)*Vector3f::dot(foundNormal, direction)/foundNormal.sqrLength() + direction; 362 363 //Vector3f v = rg.getUniform3D(); 364 //if (Vector3f::dot(v,nDir)<0) v = -v; 365 //nDir = v+nDir; 366 367 Vector3f color = rayCast(iPoint, nDir, bestObj, level+1); 368 foundColor[0] = foundColor[0]*(1-reflection) + color.x()*reflection; 369 foundColor[1] = foundColor[1]*(1-reflection) + color.y()*reflection; 370 foundColor[2] = foundColor[2]*(1-reflection) + color.z()*reflection; 371 372 } 373 374 color = Vector3f(foundColor[0],foundColor[1],foundColor[2]); 375 return color; 376 } else { 377 color = Vector3f(backgroundColor.x(),backgroundColor.y(),backgroundColor.z()); 378 return color; 379 } 380 } 381 setObjects(int count)382 void RenderThread::setObjects(int count) { 383 rayIDs = QVector<int>(count, -1); 384 } 385 386 } 387 } 388 389