/*! SLRay::refractMC scatters a ray around perfect transmissive direction according to translucency (for higher translucency the ray is less scattered). This is used for path tracing and distributed ray tracing as well as for photon scattering. The direction is calculated the same as with specular scattering (see reflectMC). The created direction is along z-axis and then transformed to lie along transmissive direction with rotationMatrix rotMat. The rotation matrix must be precalculated (stays the same for each ray sample, needs to be be calculated only once) */ void SLRay::refractMC(SLRay* refracted,SLMat3f rotMat) { SLfloat eta1, eta2; SLVec3f randVec; SLfloat translucency = hitMat->translucency(); //scatter within transmissive lobe eta1 = (SLfloat)random->Random(); eta2 = SL_2PI*(SLfloat)random->Random(); SLfloat f1=sqrt(1.0f-pow(eta1,2.0f/(translucency+1.0f))); //transform to cartesian randVec.set( f1*cos(eta2), f1*sin(eta2), pow(eta1,1.0f/(translucency+1.0f))); //ray needs to be reset if already hit a shape if(refracted->hitShape) { refracted->length = SL_FLOAT_MAX; refracted->hitShape = 0; refracted->hitPoint = SLVec3f::ZERO; refracted->hitNormal = SLVec3f::ZERO; } refracted->setDir(rotMat*randVec); }
/*! SLCurveBezier::subdivideRender adds points along the curve to the point vector renderPoints by recursively subdividing the curve with the Casteljau scheme. */ void SLCurveBezier::subdivideRender(SLVVec3f &renderPoints, const SLMat4f &wm, SLfloat epsilon, const SLVec3f& P0, const SLVec3f& P1, const SLVec3f& P2, const SLVec3f& P3) { // add first point transformed by wm if not already in the list if (renderPoints.size()==0) renderPoints.push_back(wm.multVec(P0)); else if (P0 != renderPoints[renderPoints.size()-1]) renderPoints.push_back(wm.multVec(P0)); // check to see if basically straight SLfloat Lmin = P0.distance(P3); SLfloat Lmax = P0.distance(P1) + P1.distance(P2) + P2.distance(P3); SLfloat diff = Lmin - Lmax; if (diff*diff < epsilon) return; // otherwise get control points for subdivision SLVec3f L1 = (P0 + P1) * 0.5f; SLVec3f H = (P1 + P2) * 0.5f; SLVec3f L2 = (L1 + H) * 0.5f; SLVec3f R2 = (P2 + P3) * 0.5f; SLVec3f R1 = (H + R2) * 0.5f; SLVec3f mid = (L2 + R1) * 0.5f; // subdivide subdivideRender(renderPoints, wm, epsilon, P0, L1, L2, mid); subdivideRender(renderPoints, wm, epsilon, mid, R1, R2, P3); }
/*! SLSphere::SLSphere ctor for spheric revolution object around the z-axis. */ SLSphere::SLSphere(SLfloat sphereRadius, SLint stacks, SLint slices, SLstring name, SLMaterial* mat) : SLRevolver(name) { assert(slices >= 3 && "Error: Not enough slices."); assert(slices > 0 && "Error: Not enough stacks."); _radius = sphereRadius; _stacks = stacks; _slices = slices; _smoothFirst = true; _smoothLast = true; _revAxis.set(0,0,1); _revPoints.reserve(stacks+1); SLfloat theta = -SL_PI; SLfloat phi = 0; SLfloat dTheta= SL_PI / stacks; for (SLint i=0; i<=stacks; ++i) { SLVec3f p; p.fromSpherical(sphereRadius, theta, phi); _revPoints.push_back(p); theta += dTheta; } buildMesh(mat); }
/*! SLCamera::calcMinMax calculates the axis alligned minimum and maximum point of the camera position and the 4 near clipping plane points. */ void SLCamera::calcMinMax(SLVec3f &minV, SLVec3f &maxV) { SLVec3f P[5]; SLfloat tanFov = tan(_fov*SL_DEG2RAD*0.5f); SLfloat tN = tanFov * _clipNear; //top near SLfloat rN = tN * _aspect; //right near // frustum pyramid lines P[0].set(0,0,0); // around near clipping plane P[1].set( rN, tN,-_clipNear); P[2].set( rN,-tN,-_clipNear); P[3].set(-rN,-tN,-_clipNear); P[4].set(-rN, tN,-_clipNear); // init min & max points minV.set( FLT_MAX, FLT_MAX, FLT_MAX); maxV.set(-FLT_MAX, -FLT_MAX, -FLT_MAX); // calc min and max point of all vertices for (SLuint i=0; i<5; ++i) { if (P[i].x < minV.x) minV.x = P[i].x; if (P[i].x > maxV.x) maxV.x = P[i].x; if (P[i].y < minV.y) minV.y = P[i].y; if (P[i].y > maxV.y) maxV.y = P[i].y; if (P[i].z < minV.z) minV.z = P[i].z; if (P[i].z > maxV.z) maxV.z = P[i].z; } }
/*! SLRay::reflectMC scatters a ray around perfect specular direction according to shininess (for higher shininess the ray is less scattered). This is used for path tracing and distributed ray tracing as well as for photon scattering. The direction is calculated according to MCCABE. The created direction is along z-axis and then transformed to lie along specular direction with rotationMatrix rotMat. The rotation matrix must be precalculated (stays the same for each ray sample, needs to be be calculated only once) */ bool SLRay::reflectMC(SLRay* reflected,SLMat3f rotMat) { SLfloat eta1, eta2; SLVec3f randVec; SLfloat shininess = hitMat->shininess(); //scatter within specular lobe eta1 = (SLfloat)random->Random(); eta2 = SL_2PI*(SLfloat)random->Random(); SLfloat f1 = sqrt(1.0f-pow(eta1, 2.0f/(shininess+1.0f))); //tranform to cartesian randVec.set(f1 * cos(eta2), f1 * sin(eta2), pow(eta1, 1.0f/(shininess+1.0f))); //ray needs to be reset if already hit a shape if(reflected->hitShape) { reflected->length = SL_FLOAT_MAX; reflected->hitShape = 0; reflected->hitPoint = SLVec3f::ZERO; reflected->hitNormal = SLVec3f::ZERO; } //apply rotation reflected->setDir(rotMat*randVec); //true if in direction of normal return (hitNormal * reflected->dir >= 0.0f); }
/*! SLRay::diffuseMC scatters a ray around hit normal (cosine distribution). This is only used for photonmapping(russian roulette). The random direction lies around z-Axis and is then transformed by a rotation matrix to lie along the normal. The direction is calculated according to MCCABE */ void SLRay::diffuseMC(SLRay* scattered) { SLVec3f randVec; SLfloat eta1,eta2,eta1sqrt; scattered->setDir(hitNormal); scattered->origin = hitPoint; scattered->depth = depth+1; depthReached = scattered->depth; // for reflectance the start material stays the same scattered->originMat = hitMat; scattered->originShape = hitShape; scattered->type = REFLECTED; //calculate rotation matrix SLMat3f rotMat; SLVec3f rotAxis((SLVec3f(0.0,0.0,1.0) ^ scattered->dir).normalize()); SLfloat rotAngle=acos(scattered->dir.z); //z*scattered.dir() rotMat.rotation(rotAngle*180.0f/SL_PI, rotAxis); //cosine distribution eta1 = (SLfloat)random->Random(); eta2 = SL_2PI*(SLfloat)random->Random(); eta1sqrt = sqrt(1-eta1); //transform to cartesian randVec.set(eta1sqrt * cos(eta2), eta1sqrt * sin(eta2), sqrt(eta1)); scattered->setDir(rotMat*randVec); }
/*!Creates photons (ray with power) from a point light source and sends it into the scene. Note that a point light has no cosine distribution and no surface area */ void SLPhotonMapper::photonEmission(SLLight* light) { SLfloat eta1,eta2,f1,f2; SLVec3f C,N; SLVec3f power; C = light->positionWS(); //because point lights have no surface use only radiance for power of emitted photons power.set(light->diffuse().r,light->diffuse().g,light->diffuse().b); //progress SLlong maxPhotons=_mapCaustic->maxStoredPhotons()+_mapGlobal->maxStoredPhotons(); SLlong curPhotons=_mapCaustic->storedPhotons()+_mapGlobal->storedPhotons();; SLlong emitted=0; //shoot the photons as long as maps are not full while(emitted<light->photons() && !(_mapCaustic->isFull()&&(_mapGlobal->isFull()))) { //progress if(emitted%1000==0) { curPhotons=_mapCaustic->storedPhotons()+_mapGlobal->storedPhotons(); printf("\b\b\b\b%3.0f%%",(SLfloat)curPhotons/(SLfloat)maxPhotons*100.0f); } //create spherical random direction eta1=_random->Random();eta2=_random->Random(); f1 = SL_2PI*eta2; f2 = 2.0f * sqrt(eta1 * (1-eta1)); //direction in cartesian coordinates N.set(cos(f1)*f2, sin(f1)*f2, (1.0-2.0*eta1)); //create and emit photon SLRay scattered(C,N,PRIMARY, (SLShape*)light, SL_FLOAT_MAX, 1); photonScatter(&scattered,power,LIGHT); emitted++; //scaling of stored photons is necessary if one of the maps was filled by this photon //(because emission of photons continues in order to fill the other map) if(_mapCaustic->isFull() && !_mapCausticGotFull) { _mapCausticGotFull = true; _mapCaustic->scalePhotonPower(1.0f/SLfloat(emitted)); } if(_mapGlobal->isFull() && !_mapGlobalGotFull) { _mapGlobalGotFull = true; _mapGlobal->scalePhotonPower(1.0f/SLfloat(emitted)); } } //scale all stored photons of this light source if(emitted) { SLRay::emittedPhotons+=emitted; _mapCaustic->scalePhotonPower(1.0f/SLfloat(emitted)); _mapGlobal->scalePhotonPower(1.0f/SLfloat(emitted)); } }
/*! SLMesh::preShade calculates the rest of the intersection information after the final hit point is determined. Should be called just before the shading when the final intersection point of the closest triangle was found. */ void SLMesh::preShade(SLRay* ray) { SLFace* hit = ray->hitTriangle; // calculate the hit point in world space ray->hitPoint.set(ray->origin + ray->length * ray->dir); // calculate the interpolated normal with vertex normals in object space ray->hitNormal.set(N[hit->iA] * (1-(ray->hitU+ray->hitV)) + N[hit->iB] * ray->hitU + N[hit->iC] * ray->hitV); // transform normal back to world space ray->hitNormal.set(ray->hitShape->wmN() * ray->hitNormal); // invert normal if the ray is inside a shape if (!ray->isOutside) ray->hitNormal *= -1; // for shading the normal is expected to be unit length ray->hitNormal.normalize(); // calculate interpolated texture coordinates SLVGLTexture& textures = ray->hitMat->textures(); if (textures.size() > 0) { SLVec2f Tu(Tc[hit->iB] - Tc[hit->iA]); SLVec2f Tv(Tc[hit->iC] - Tc[hit->iA]); SLVec2f tc(Tc[hit->iA] + ray->hitU*Tu + ray->hitV*Tv); ray->hitTexCol.set(textures[0]->getTexelf(tc.x,tc.y)); // bumpmapping if (textures.size() > 1) { if (T) { // calculate the interpolated tangent with vertex tangent in object space SLVec4f hitT(T[hit->iA] * (1-(ray->hitU+ray->hitV)) + T[hit->iB] * ray->hitU + T[hit->iC] * ray->hitV); SLVec3f T3(hitT.x,hitT.y,hitT.z); // tangent with 3 components T3.set(ray->hitShape->wmN() * T3); // transform tangent back to world space SLVec2f d = textures[1]->dsdt(tc.x,tc.y); // slope of bumpmap at tc SLVec3f N = ray->hitNormal; // unperturbated normal SLVec3f B(N^T3); // binormal tangent B B *= T[hit->iA].w; // correct handedness SLVec3f D(d.x*T3 + d.y*B); // perturbation vector D N+=D; N.normalize(); ray->hitNormal.set(N); } } } }
/*! SLMesh::calcMinMax calculates the axis alligned minimum and maximum point */ void SLMesh::calcMinMax(SLVec3f &minV, SLVec3f &maxV) { // init min & max points minV.set( FLT_MAX, FLT_MAX, FLT_MAX); maxV.set(-FLT_MAX, -FLT_MAX, -FLT_MAX); // calc min and max point of all vertices for (SLuint i=0; i<numV; ++i) { if (P[i].x < minV.x) minV.x = P[i].x; if (P[i].x > maxV.x) maxV.x = P[i].x; if (P[i].y < minV.y) minV.y = P[i].y; if (P[i].y > maxV.y) maxV.y = P[i].y; if (P[i].z < minV.z) minV.z = P[i].z; if (P[i].z > maxV.z) maxV.z = P[i].z; } }
/*! Rotates the object so that it's forward vector is pointing towards the 'target' point. Default forward is -Z. The 'relativeTo' parameter defines in what space the 'target' parameter is to be interpreted in. */ void SLNode::lookAt(const SLVec3f& target, const SLVec3f& up, SLTransformSpace relativeTo) { SLVec3f pos = translation(); SLVec3f dir; SLVec3f localUp = up; if (relativeTo == TS_world && _parent) { SLVec3f localTarget = _parent->updateAndGetWMI() * target; localUp = _parent->updateAndGetWMI().mat3() * up; dir = localTarget - translation(); } else if (relativeTo == TS_object) dir = _om * target - translation(); else dir = target - translation(); dir.normalize(); SLfloat cosAngle = localUp.dot(dir); // dir and up are parallel and facing in the same direction // or facing in opposite directions. // in this case we just rotate the up vector by 90° around // our current right vector // @todo This check might make more sense to be in Mat3.posAtUp if (fabs(cosAngle-1.0) <= FLT_EPSILON || fabs(cosAngle+1.0) <= FLT_EPSILON) { SLMat3f rot; rot.rotation(-90.0f, right()); localUp = rot * localUp; } _om.posAtUp(pos, pos+dir, localUp); needUpdate(); }
SLfloat SLLightRect::shadowTestMC(SLRay* ray, // ray of hit point const SLVec3f& L, // vector from hit point to light const SLfloat lightDist) // distance to light { SLVec3f SP; // vector hit point to sample point in world coords SLfloat randX = rnd01(); SLfloat randY = rnd01(); // choose random point on rect as sample SP.set(updateAndGetWM().multVec(SLVec3f((randX*_width)-(_width*0.5f), (randY*_height)-(_height*0.5f), 0)) - ray->hitPoint); SLfloat SPDist = SP.length(); SP.normalize(); SLRay shadowRay(SPDist, SP, ray); SLScene::current->root3D()->hitRec(&shadowRay); if (shadowRay.length >= SPDist - FLT_EPSILON) return 1.0f; else return 0.0f; }
/*! SLCurveBezier::subdivideLength calculates length of Bezier curve by recursive midpoint subdivision. */ SLfloat SLCurveBezier::subdivideLength(const SLVec3f& P0, const SLVec3f& P1, const SLVec3f& P2, const SLVec3f& P3) { // check to see if basically straight SLfloat Lmin = P0.distance(P3); SLfloat Lmax = P0.distance(P1) + P1.distance(P2) + P2.distance(P3); SLfloat diff = Lmin - Lmax; if (diff*diff < 1.0e-3f) return 0.5f*(Lmin + Lmax); // otherwise get control points for subdivision SLVec3f L1 = (P0 + P1) * 0.5f; SLVec3f H = (P1 + P2) * 0.5f; SLVec3f L2 = (L1 + H ) * 0.5f; SLVec3f R2 = (P2 + P3) * 0.5f; SLVec3f R1 = (H + R2) * 0.5f; SLVec3f mid = (L2 + R1) * 0.5f; // subdivide return subdivideLength(P0, L1, L2, mid) + subdivideLength(mid, R1, R2, P3); }
//----------------------------------------------------------------------------- //! Gets called whenever the mouse is moved. SLbool SLCamera::onMouseMove(const SLMouseButton button, const SLint x, const SLint y, const SLKey mod) { if (button == ButtonLeft) //================================================ { // new vars needed SLVec3f position = this->translation(); SLVec3f forward = this->forward(); SLVec3f right = this->right(); SLVec3f up = this->up(); // The lookAt point SLVec3f laP = position + _focalDist * forward; // Determine rotation point as the center of the AABB of the hitNode SLVec3f rtP; if (_lookAtRay.length < FLT_MAX && _lookAtRay.hitNode) rtP = _lookAtRay.hitNode->aabb()->centerWS(); else rtP = laP; // Determine rot angles around x- & y-axis SLfloat dY = (y-_oldTouchPos1.y) * _rotFactor; SLfloat dX = (x-_oldTouchPos1.x) * _rotFactor; if (_camAnim==turntableYUp) //....................................... { SLMat4f rot; rot.translate(rtP); rot.rotate(-dX, SLVec3f(0,1,0)); rot.rotate(-dY, right); rot.translate(-rtP); _om.setMatrix(rot * _om); needWMUpdate(); } else if (_camAnim==turntableZUp) //.................................. { SLMat4f rot; rot.translate(rtP); rot.rotate(dX, SLVec3f(0,0,1)); rot.rotate(dY, right); rot.translate(-rtP); _om.setMatrix(rot * _om); needWMUpdate(); } else if (_camAnim==walkingYUp) //.................................... { dY *= 0.5f; dX *= 0.5f; SLMat4f rot; rot.rotate(-dX, SLVec3f(0, 1, 0)); rot.rotate(-dY, right); forward.set(rot.multVec(forward)); lookAt(position + forward); } else if (_camAnim==walkingZUp) //.................................... { dY *= 0.5f; dX *= 0.5f; SLMat4f rot; rot.rotate(-dX, SLVec3f(0, 0, 1)); rot.rotate(-dY, right); forward.set(rot.multVec(forward)); lookAt(position + forward, SLVec3f(0, 0, 1)); } _oldTouchPos1.set((SLfloat)x,(SLfloat)y); } else if (button == ButtonMiddle) //============================================== { if (_camAnim==turntableYUp || _camAnim==turntableZUp) { // Calculate the fraction delta of the mouse movement SLVec2f dMouse(x-_oldTouchPos1.x, _oldTouchPos1.y-y); dMouse.x /= (SLfloat)_scrW; dMouse.y /= (SLfloat)_scrH; // Scale the mouse delta by the lookAt distance SLfloat lookAtDist; if (_lookAtRay.length < FLT_MAX) lookAtDist = _lookAtRay.length; else lookAtDist = _focalDist; // scale factor depending on the space sice at focal dist SLfloat spaceH = tan(SL_DEG2RAD*_fov/2) * lookAtDist * 2.0f; SLfloat spaceW = spaceH * _aspect; dMouse.x *= spaceW; dMouse.y *= spaceH; if (mod==KeyCtrl) { translate(SLVec3f(-dMouse.x, 0, dMouse.y), TS_Object); } else { translate(SLVec3f(-dMouse.x, -dMouse.y, 0), TS_Object); } _oldTouchPos1.set((SLfloat)x,(SLfloat)y); } } //======================================================================== return true; }
/*! SLCamera::camUpdate does the smooth transition for the walk animation. It is called in every frame. It moves the camera after the key was released and smoothly stops the motion by decreasing the speed every frame. */ SLbool SLCamera::camUpdate(SLfloat elapsedTimeMS) { if (_velocity == SLVec3f::ZERO && _moveDir == SLVec3f::ZERO) return false; SLfloat dtS = elapsedTimeMS * 0.001f; SLbool braking = false; if (_moveDir != SLVec3f::ZERO) { // x and z movement direction vector should be projected on the x,z plane while // but still in local space // the y movement direction should alway be in world space SLVec3f f = forward(); f.y = 0; f.normalize(); SLVec3f r = right(); r.y = 0; r.normalize(); _acceleration = f * -_moveDir.z + r * _moveDir.x; _acceleration.y = _moveDir.y; _acceleration.normalize(); _acceleration *= _moveAccel; } // accelerate in the opposite velocity to brake else { _acceleration = -_velocity.normalized() * _brakeAccel; braking = true; } // accelerate SLfloat velMag = _velocity.length(); SLVec3f increment = _acceleration * dtS; // all units in m/s, convert MS to S // early out if we're braking and the velocity would fall < 0 if (braking && increment.lengthSqr() > _velocity.lengthSqr()) { _velocity.set(SLVec3f::ZERO); return false; } _velocity += increment - _drag * _velocity * dtS; velMag = _velocity.length(); // don't go over max speed if (velMag > _maxSpeed) _velocity = _velocity.normalized() * _maxSpeed; // final delta movement vector SLVec3f delta = _velocity * dtS; // adjust for scaling (if the character is shrinked or enlarged) delta *= _unitScaling; translate(delta, TS_World); return true; //SL_LOG("cs: %3.2f | %3.2f, %3.2f, %3.2f\n", _velocity.length(), _acceleration.x, _acceleration.y, _acceleration.z); /* OLD CODE BELOW // ToDo: The recursive update traversal is not yet implemented if (_maxSpeed != SLVec3f::ZERO || _curSpeed != SLVec3f::ZERO) { // delta speed during acceleration/slow down SLfloat ds = _speedLimit / 20.0f; // Accelerate if (_maxSpeed.x>0 && _curSpeed.x<_maxSpeed.x) _curSpeed.x += ds; else if (_maxSpeed.x<0 && _curSpeed.x>_maxSpeed.x) _curSpeed.x -= ds; if (_maxSpeed.y>0 && _curSpeed.y<_maxSpeed.y) _curSpeed.y += ds; else if (_maxSpeed.y<0 && _curSpeed.y>_maxSpeed.y) _curSpeed.y -= ds; if (_maxSpeed.z>0 && _curSpeed.z<_maxSpeed.z) _curSpeed.z += ds; else if (_maxSpeed.z<0 && _curSpeed.z>_maxSpeed.z) _curSpeed.z -= ds; if (_curSpeed.z == 0.0f) { int i = 0; } // Slow down if (_maxSpeed.z == 0) { if (_curSpeed.z > 0) { _curSpeed.z -= ds; if (_curSpeed.z < 0) _curSpeed.z = 0.0f; } else if (_curSpeed.z < 0) { _curSpeed.z += ds; if (_curSpeed.z > 0) _curSpeed.z = 0.0f; } } if (_maxSpeed.x == 0) { if (_curSpeed.x < 0) { _curSpeed.x += ds; if (_curSpeed.x > 0) _curSpeed.x = 0.0f; } else if (_curSpeed.x > 0) { _curSpeed.x -= ds; if (_curSpeed.x < 0) _curSpeed.x = 0.0f; } } if (_maxSpeed.y == 0) { if (_curSpeed.y < 0) { _curSpeed.y += ds; if (_curSpeed.y > 0) _curSpeed.y = 0.0f; } else if (_curSpeed.y > 0) { _curSpeed.y -= ds; if (_curSpeed.y < 0) _curSpeed.y = 0.0f; } } SL_LOG("cs: %3.1f, %3.1f, %3.1f\n", _curSpeed.x, _curSpeed.y, _curSpeed.z); SLfloat temp = _curSpeed.length(); _curSpeed = updateAndGetWM().mat3() * _curSpeed; _curSpeed.y = 0; _curSpeed.normalize(); _curSpeed *= temp; forward(); SLVec3f delta(_curSpeed * elapsedTimeMS / 1000.0f); translate(delta, TS_World); return true; } return false;*/ }
/*! SLMesh::calcTangents computes the tangent and bi-tangent per vertex used for GLSL normal map bumb mapping. The code and mathematical derivation is in detail explained in: http://www.terathon.com/code/tangent.html */ void SLMesh::calcTangents() { if (P && N && Tc) { // allocat tangents delete[] T; T = new SLVec4f[numV]; // allocate temp arrays for tangents SLVec3f* T1 = new SLVec3f[numV * 2]; SLVec3f* T2 = T1 + numV; memset(T1, 0, numV * sizeof(SLVec3f) * 2); for (SLuint m = 0; m < numM; ++m) { for (SLuint f = 0; f < M[m].numF; ++f) { // Get the 3 vertex indexes SLushort iVA = F[M[m].startF + f].iA; SLushort iVB = F[M[m].startF + f].iB; SLushort iVC = F[M[m].startF + f].iC; float x1 = P[iVB].x - P[iVA].x; float x2 = P[iVC].x - P[iVA].x; float y1 = P[iVB].y - P[iVA].y; float y2 = P[iVC].y - P[iVA].y; float z1 = P[iVB].z - P[iVA].z; float z2 = P[iVC].z - P[iVA].z; float s1 = Tc[iVB].x - Tc[iVA].x; float s2 = Tc[iVC].x - Tc[iVA].x; float t1 = Tc[iVB].y - Tc[iVA].y; float t2 = Tc[iVC].y - Tc[iVA].y; float r = 1.0F / (s1*t2 - s2*t1); SLVec3f sdir((t2*x1 - t1*x2) * r, (t2*y1 - t1*y2) * r, (t2*z1 - t1*z2) * r); SLVec3f tdir((s1*x2 - s2*x1) * r, (s1*y2 - s2*y1) * r, (s1*z2 - s2*z1) * r); T1[iVA] += sdir; T1[iVB] += sdir; T1[iVC] += sdir; T2[iVA] += tdir; T2[iVB] += tdir; T2[iVC] += tdir; } } for (SLuint i=0; i < numV; ++i) { // Gram-Schmidt orthogonalize T[i] = T1[i] - N[i] * N[i].dot(T1[i]); T[i].normalize(); // Calculate temp. bitangent and store its handedness in T.w SLVec3f bitangent; bitangent.cross(N[i], T1[i]); T[i].w = (bitangent.dot(T2[i]) < 0.0f) ? -1.0f : 1.0f; } delete[] T1; } }
/*! onInit initializes the global variables and builds the shader program. It should be called after a window with a valid OpenGL context is present. */ void onInit() { // Set light parameters _globalAmbi.set(0.0f, 0.0f, 0.0f); _lightPos.set( 0.0f, 0.0f, 100.0f); _lightDir.set( 0.0f, 0.0f,-1.0f); _lightAmbient.set( 0.1f, 0.1f, 0.1f); _lightDiffuse.set( 1.0f, 1.0f, 1.0f); _lightSpecular.set( 1.0f, 1.0f, 1.0f); _matAmbient.set( 1.0f, 1.0f, 1.0f); _matDiffuse.set( 1.0f, 1.0f, 1.0f); _matSpecular.set( 1.0f, 1.0f, 1.0f); _matEmissive.set( 0.0f, 0.0f, 0.0f); _matShininess = 100.0f; // backwards movement of the camera _camZ = -3.0f; // Mouse rotation parameters _rotX = 0; _rotY = 0; _deltaX = 0; _deltaY = 0; _mouseLeftDown = false; // Load textures _textureID = glUtils::buildTexture("../_data/images/textures/earth2048_C.jpg"); // Load, compile & link shaders _shaderVertID = glUtils::buildShader("../_data/shaders/ADSTex.vert", GL_VERTEX_SHADER); _shaderFragID = glUtils::buildShader("../_data/shaders/ADSTex.frag", GL_FRAGMENT_SHADER); _shaderProgID = glUtils::buildProgram(_shaderVertID, _shaderFragID); // Activate the shader program glUseProgram(_shaderProgID); // Get the variable locations (identifiers) within the vertex & pixel shader programs _pLoc = glGetAttribLocation (_shaderProgID, "a_position"); _nLoc = glGetAttribLocation (_shaderProgID, "a_normal"); _tLoc = glGetAttribLocation (_shaderProgID, "a_texCoord"); _mvMatrixLoc = glGetUniformLocation(_shaderProgID, "u_mvMatrix"); _mvpMatrixLoc = glGetUniformLocation(_shaderProgID, "u_mvpMatrix"); _nMatrixLoc = glGetUniformLocation(_shaderProgID, "u_nMatrix"); _globalAmbiLoc = glGetUniformLocation(_shaderProgID, "u_globalAmbi"); _lightPosVSLoc = glGetUniformLocation(_shaderProgID, "u_lightPosVS"); _lightDirVSLoc = glGetUniformLocation(_shaderProgID, "u_lightDirVS"); _lightAmbientLoc = glGetUniformLocation(_shaderProgID, "u_lightAmbient"); _lightDiffuseLoc = glGetUniformLocation(_shaderProgID, "u_lightDiffuse"); _lightSpecularLoc= glGetUniformLocation(_shaderProgID, "u_lightSpecular"); _matAmbientLoc = glGetUniformLocation(_shaderProgID, "u_matAmbient"); _matDiffuseLoc = glGetUniformLocation(_shaderProgID, "u_matDiffuse"); _matSpecularLoc = glGetUniformLocation(_shaderProgID, "u_matSpecular"); _matEmissiveLoc = glGetUniformLocation(_shaderProgID, "u_matEmissive"); _matShininessLoc = glGetUniformLocation(_shaderProgID, "u_matShininess"); _texture0Loc = glGetUniformLocation(_shaderProgID, "u_texture0"); // Build object buildSquare(); // Set some OpenGL states glClearColor(0.0f, 0.0f, 0.0f, 1); // Set the background color glEnable(GL_DEPTH_TEST); // Enables depth test glEnable(GL_CULL_FACE); // Enables the culling of back faces GETGLERROR; // Check for OpenGL errors }
/*! SLLightRect::shadowTest returns 0.0 if the hit point is completely shaded and 1.0 if it is 100% lighted. A return value inbetween is calculate by the ratio of the shadow rays not blocked to the total number of casted shadow rays. */ SLfloat SLLightRect::shadowTest(SLRay* ray, // ray of hit point const SLVec3f& L, // vector from hit point to light const SLfloat lightDist) // distance to light { if (_samples.x==1 && _samples.y==1) { // define shadow ray SLRay shadowRay(lightDist, L, ray); SLScene::current->root3D()->hitRec(&shadowRay); return (shadowRay.length < lightDist) ? 0.0f : 1.0f; } else // do light sampling for soft shadows { SLfloat dw = (SLfloat)_width/(SLfloat)_samples.x; // width of a sample cell SLfloat dl = (SLfloat)_height/(SLfloat)_samples.y;// length of a sample cell SLint x, y, hx=_samples.x/2, hy=_samples.y/2; SLint samples = _samples.x*_samples.y; SLbool* isSampled = new SLbool[samples]; SLbool importantPointsAreLighting = true; SLfloat lighted = 0.0f; // return value SLfloat invSamples = 1.0f/(SLfloat)(samples); SLVec3f SP; // vector hitpoint to samplepoint in world coords for (y=0; y<_samples.y; ++y) { for (x=0; x<_samples.x; ++x) { SLint iSP = y*_samples.x + x; isSampled[iSP]=false; } } /* Important sample points (X) on a 7 by 5 rectangular light. If all of them are lighting the hitpoint the sample points in between (O) are not tested anymore. 0 1 2 3 4 5 6 +---+---+---+---+---+---+---+ 0 | X | . | . | X | . | . | X | +---+---+---+---+---+---+---+ 1 | . | . | . | . | . | . | . | +---+---+---+---+---+---+---+ 2 | X | . | . | X | . | . | X | +---+---+---+---+---+---+---+ 3 | . | . | . | . | . | . | . | +---+---+---+---+---+---+---+ 4 | X | . | . | X | . | . | X | +---+---+---+---+---+---+---+ */ // Double loop for the important sample points for (y=-hy; y<=hy; y+=hy) { for (x=-hx; x<=hx; x+=hx) { SLint iSP = (y+hy)*_samples.x + x+hx; isSampled[iSP]=true; SP.set(updateAndGetWM().multVec(SLVec3f(x*dw, y*dl, 0)) - ray->hitPoint); SLfloat SPDist = SP.length(); SP.normalize(); SLRay shadowRay(SPDist, SP, ray); SLScene::current->root3D()->hitRec(&shadowRay); if (shadowRay.length >= SPDist-FLT_EPSILON) lighted += invSamples; // sum up the light else importantPointsAreLighting = false; } } if (importantPointsAreLighting) lighted = 1.0f; else { // Double loop for the samplepoints inbetween for (y=-hy; y<=hy; ++y) { for (x=-hx; x<=hx; ++x) { SLint iSP = (y+hy)*_samples.x + x+hx; if (!isSampled[iSP]) { SP.set(updateAndGetWM().multVec(SLVec3f(x*dw, y*dl, 0)) - ray->hitPoint); SLfloat SPDist = SP.length(); SP.normalize(); SLRay shadowRay(SPDist, SP, ray); SLScene::current->root3D()->hitRec(&shadowRay); // sum up the light if (shadowRay.length >= SPDist-FLT_EPSILON) lighted += invSamples; } } } } if (isSampled) delete [] isSampled; return lighted; } }