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