Tessellation
Unity で距離に応じたテッセレーションを行ってみた - 凹みTips (hecomi.com)
http://tinkering.ee/unity/asset-unity-refractive-shader/
https://github.com/MaxwellGengYF/Unity-Tessellation-PBR-Shaders
Sample A
/*
//
// Base Classes to easy WebGL lists management
//
*/
function myObject3D(){
var Vertices = [];
var Normals = [];
var Tangents = [];
var TexCoords = [];
var TriIndex = [];
var IsEdge = [];
var NbTriangles = 0;
}
myObject3D.prototype.Clear = function()
{
this.Vertices = [];
this.Normals = [];
this.Tangents = [];
this.TexCoords = [];
this.TriIndex = [];
this.IsEdge = [];
this.NbTriangles = 0;
}
myObject3D.prototype.AppendLists = function(b,lastNbOfPoints )
{
for (var k=0;k<b.Vertices.length;k++){
this.Vertices.push(b.Vertices[k]);
this.Normals.push(b.Normals[k]);
}
for (var k=0;k<b.Tangents.length;k++){
this.Tangents.push(b.Tangents[k]);
}
for (var k=0;k<b.TexCoords.length;k++){
this.TexCoords.push(b.TexCoords[k]);
}
for (var k=0;k<b.IsEdge.length;k++){
this.IsEdge.push(b.IsEdge[k]);
}
var nbPts = lastNbOfPoints;
for (var k=0;k<b.TriIndex.length;k++){
var tmp = b.TriIndex[k];
tmp+=(nbPts);
//console.log('tmp='+tmp);
this.TriIndex.push(tmp);
}
}
// look up base triangle per base triangle in uv space
// among the edges and not the end of segments
myObject3D.prototype.Snap = function(delta)
{
var TexCoord = [];
for (var j=0;j<this.TexCoords.length/2;j++){
TexCoord.push(this.TexCoords[2*j]);
TexCoord.push(this.TexCoords[2*j+1]);
if (this.IsEdge[j]==0){
//TexCoord[2*j] = 0;
//TexCoord[2*j+1] = 0;
this.Vertices[3*j] = 0;
this.Vertices[3*j+1] = 0;
this.Vertices[3*j+2] = 0;
}
}
/*
for (var j=0;j<this.TexCoords.length/2;j++){
var vJx=this.TexCoords[2*j];
var vJy=this.TexCoords[2*j+1];
if (this.IsEdge[j]){
for (var i=0;i<this.TexCoords.length/2;i++){
var vIx=this.TexCoords[2*i];
var vIy=this.TexCoords[2*i+1];
if (this.IsEdge[i]){
//console.log('index i='+i+' index j='+j);
if ( (Math.abs(vJx-vIx)<delta)&&(Math.abs(vJy-vIy)<delta) ){
var a = (this.Vertices[3*i]+this.Vertices[3*j] )/2;
var b = (this.Vertices[3*i+1]+this.Vertices[3*j+1] )/2;
var c = (this.Vertices[3*i+2]+this.Vertices[3*j+2] )/2;
//bad
this.Vertices[3*i] = a;
this.Vertices[3*i+1] = b;
this.Vertices[3*i+2] = c;
this.Vertices[3*j] = a;
this.Vertices[3*j+1] = b;
this.Vertices[3*j+2] = c;
this.TexCoords[2*i] = 0;
this.TexCoords[2*i+1] =0;
this.TexCoords[2*j] = 0;
this.TexCoords[2*j+1] =0;
//end bad
TexCoord[2*i] = 0;
TexCoord[2*i+1] = 0;
TexCoord[2*j] = 0;
TexCoord[2*j+1] = 0;
}
}
}
}
}
*/
this.TexCoords = TexCoord;
console.log('[DBG] snapped');
}
myObject3D.prototype.ComputeTangents = function()
{
function assignVec3(vec,a,idx){
vec[0]=a[3*idx+0];
vec[1]=a[3*idx+1];
vec[2]=a[3*idx+2];
}
function assignVec2(vec,a,idx){
vec[0]=a[2*idx+0];
vec[1]=a[2*idx+1];
}
function dot(a,b){
return (a[0]*b[0]+a[1]*b[1]+a[2]*b[2]);
}
function cross(a,b){
var rtn=vec3.create();
rtn[0]=a[1]*b[2]-a[2]*b[1];
rtn[1]=a[2]*b[0]-a[0]*b[2];
rtn[2]=a[0]*b[1]-a[1]*b[0];
return rtn;
}
function Normalize(a){
//log('Norm Of:'+a[0]+' '+a[1]+' '+a[2]);
var rth = vec3.create();
var no = Math.sqrt(a[0]*a[0]+a[1]*a[1]+a[2]*a[2]);
var n;
if (no == 0){
log('Norm Of:'+a[0]+' '+a[1]+' '+a[2]);
//n =0;
rth[0]=-1;
rth[1]=0;
rth[2]=0;
return rth;
}else{
n = 1.0/no;
}
//log('n:'+n);
rth[0] = a[0]*n;
rth[1] = a[1]*n;
rth[2] = a[2]*n;
//log('Norm Of:'+rth[0]+' '+rth[1]+' '+rth[2]);
return rth;
}
var vtxCount = this.Vertices.length/3;
console.log('vtxCount:'+this.Vertices.length);
//vtxCount = 6;
var triCount = this.TriIndex.length/3;
console.log('vtxCount:'+vtxCount);
console.log('triCount:'+triCount);
var tan1 = [];
var tan2 = [];
var tangents =[];
for (var a =0; a< vtxCount;a++){
tan1[3*a]=0;
tan1[3*a+1]=0;
tan1[3*a+2]=0;
tan2[3*a]=0;
tan2[3*a+1]=0;
tan2[3*a+2]=0;
}
for (var a =0; a< triCount;a++){
var i1 = this.TriIndex[3*a+0];
var i2 = this.TriIndex[3*a+1];
var i3 = this.TriIndex[3*a+2];
// log(' '+i1+' '+i2+' '+i3);
var v1 = vec3.create();
var v2 = vec3.create();
var v3 = vec3.create();
assignVec3(v1,this.Vertices,i1);
assignVec3(v2,this.Vertices,i2);
assignVec3(v3,this.Vertices,i3);
var w1= [],w2=[],w3=[];
assignVec2(w1,this.TexCoords,i1);
assignVec2(w2,this.TexCoords,i2);
assignVec2(w3,this.TexCoords,i3);
// log('v1: '+v1[0]+' '+v1[1]+' '+v1[2]);
// log('v2: '+v2[0]+' '+v2[1]+' '+v2[2]);
// log('v3: '+v3[0]+' '+v3[1]+' '+v3[2]);
//log('w1: '+w1[0]+' '+w1[1]);
var x1 = v2[0]-v1[0];
var x2 = v3[0]-v1[0];
var y1 = v2[1]-v1[1];
var y2 = v3[1]-v1[1];
var z1 = v2[2]-v1[2];
var z2 = v3[2]-v1[2];
var s1 = w2[0]-w1[0];
var s2 = w3[0]-w1[0];
var t1 = w2[1]-w1[1];
var t2 = w3[1]-w1[1];
/*
log('s1: '+s1+' s2: '+s2);
log('t1: '+t1+' t2: '+t2);
log('s1*t2: '+s1*t2);
log('-s2*t1: '+s2*t1);
*/
var rr = (s1*t2-s2*t1);
if (rr==0){
log('rr is NULL');
}
var r = 1.0 / rr ;
sdir = vec3.create();
tdir = vec3.create();
sdir[0]=(t2 * x1 - t1 * x2) * r;
sdir[1]=(t2 * y1 - t1 * y2) * r;
sdir[2]=(t2 * z1 - t1 * z2) * r;
tdir[0]=(s1 * x2 - s2 * x1) * r;
tdir[1]=(s1 * y2 - s2 * y1) * r;
tdir[2]=(s1 * z2 - s2 * z1) * r;
tan1[3*i1]+=sdir[0];
tan1[3*i1+1]+=sdir[1];
tan1[3*i1+2]+=sdir[2];
tan2[3*i1]+=tdir[0];
tan2[3*i1+1]+=tdir[1];
tan2[3*i1+2]+=tdir[2];
tan1[3*i2]+=sdir[0];
tan1[3*i2+1]+=sdir[1];
tan1[3*i2+2]+=sdir[2];
tan2[3*i2]+=tdir[0];
tan2[3*i2+1]+=tdir[1];
tan2[3*i2+2]+=tdir[2];
tan1[3*i3]+=sdir[0];
tan1[3*i3+1]+=sdir[1];
tan1[3*i3+2]+=sdir[2];
tan2[3*i3]+=tdir[0];
tan2[3*i3+1]+=tdir[1];
tan2[3*i3+2]+=tdir[2];
}
for (var a =0; a< vtxCount;a++){
var n = vec3.create();
assignVec3(n,this.Normals,a);
var t = vec3.create();
t[0]=tan1[3*a];
t[1]=tan1[3*a+1];
t[2]=tan1[3*a+2];
var f = dot(n,t);
//u = cross(t,u);
var tangent = vec3.create();
tangent[0]=t[0]-n[0]*f;
tangent[1]=t[1]-n[1]*f;
tangent[2]=t[2]-n[2]*f;
var tan2_a = vec3.create();
tan2_a[0]=tan2[3*a];
tan2_a[1]=tan2[3*a+1];
tan2_a[2]=tan2[3*a+2];
var k = dot(cross(n,t),tan2_a);
var w;
if (k<0.0){
w = -1.0;
}else{
w= 1.0;
}
//log('tangent ['+a+']: '+tangent[0]+' '+tangent[1]+' '+tangent[2]+' '+w);
tangent = Normalize(tangent);
//log('tangent Normed ['+a+']: '+tangent[0]+' '+tangent[1]+' '+tangent[2]+' '+w);
tangents[4*a]=tangent[0];
tangents[4*a+1]=tangent[1];
tangents[4*a+2]=tangent[2];
tangents[4*a+3]=w;
}
this.Tangents = tangents;
}
myObject3D.prototype.SetListsForGL = function(gl)
{
// assign points
triangleVertexIdxPositionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, triangleVertexIdxPositionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(this.Vertices), gl.STATIC_DRAW);
triangleVertexIdxPositionBuffer.itemSize = 3;
triangleVertexIdxPositionBuffer.numItems = this.Vertices.length/3;
// assign textures cords
triangleVertexTextureCoordBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, triangleVertexTextureCoordBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(this.TexCoords), gl.STATIC_DRAW);
triangleVertexTextureCoordBuffer.itemSize = 2;
triangleVertexTextureCoordBuffer.numItems = this.TexCoords.length/2;
// assign normals
triangleVertexNormalBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, triangleVertexNormalBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(this.Normals), gl.STATIC_DRAW);
triangleVertexNormalBuffer.itemSize = 3;
triangleVertexNormalBuffer.numItems = this.Normals.length/3;
// assign normals
triangleVertexTangentBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, triangleVertexTangentBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(this.Tangents), gl.STATIC_DRAW);
triangleVertexTangentBuffer.itemSize = 4;
triangleVertexTangentBuffer.numItems = this.Tangents.length/4;
//assign the indexed triangles
triangleVertexIndexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, triangleVertexIndexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(this.TriIndex), gl.STATIC_DRAW);
triangleVertexIndexBuffer.itemSize = 1;
triangleVertexIndexBuffer.numItems = this.TriIndex.length;
//Show report
//console.log(' Vertices No:'+Vertex.length/3);
//console.log(' Normals No:'+obj.vertexNormals.length/3);
//console.log(' Indexed Triangles No:'+indexTr.length/3);
}
/*
// Vertex, Vertex3D,Vertex4D defined somewhere else
*/
function BaseTriangle2(FaceIndex,ins_obj3D)
{
this.Model = ins_obj3D;
this.FaceIndex = FaceIndex;
this.I0 = this.Model.TriIndex[FaceIndex*3];
this.I1 = this.Model.TriIndex[FaceIndex*3+1];
this.I2 = this.Model.TriIndex[FaceIndex*3+2];
// Vertex
this.ST0 = this.FetchTextCoord(this.I0);
this.ST1 = this.FetchTextCoord(this.I1);
this.ST2 = this.FetchTextCoord(this.I2);
// Vertex3D
this.P0 = this.FetchPosition(this.I0);
this.P1 = this.FetchPosition(this.I1);
this.P2 = this.FetchPosition(this.I2);
// Vertex3D
this.N0 = this.FetchNormal(this.I0);
this.N1 = this.FetchNormal(this.I1);
this.N2 = this.FetchNormal(this.I2);
// Vertex4D
this.T0 = this.FetchTangent(this.I0);
this.T1 = this.FetchTangent(this.I1);
this.T2 = this.FetchTangent(this.I2);
this.AttachedLst = [];
this.AttachedLstInfo = [];
}
BaseTriangle2.prototype.Show = function()
{
// Vertex
console.log('TexCoords');
this.ST0.Show();
this.ST1.Show();
this.ST2.Show();
// Vertex3D
console.log('Positions');
this.P0.Show();
this.P1.Show();
this.P2.Show();
// Vertex3D
console.log('Normals');
this.N0.Show();
this.N1.Show();
this.N2.Show();
// Vertex3D
console.log('Tangents');
this.T0.Show();
this.T1.Show();
this.T2.Show();
}
// we compute the triangle coordonates from the texture coords (core of the tessation method)
BaseTriangle2.prototype.ComputeTriangleCoords = function (s,t)
{
var rtn = new Vertex(0,0);
var A = this.ST0;
var B = this.ST1;
var C = this.ST2;
//rejection test:
var sMax = Math.max(A.s,Math.max(B.s,C.s));
var tMax = Math.max(A.t,Math.max(B.t,C.t));
var sMin = Math.min(A.s,Math.min(B.s,C.s));
var tMin = Math.min(A.t,Math.min(B.t,C.t));
/*
console.log('xMax:'+xMax);
console.log('xMin:'+xMin);
console.log('yMax:'+yMax);
console.log('yMin:'+yMin);
*/
/*
if ( (s>sMin) && (s<sMax) && ( t>tMin) && (t<tMax) ){
//nothing
}else{
return false;
}
*/
var P = new Vertex(s,t);
function dot( vA, vB){
return (vA.s*vB.s+vA.t*vB.t);
}
function minus( vA, vB){
return new Vertex(vA.s-vB.s,vA.t-vB.t);
}
// Compute vectors
v0 = minus( C , A);
v1 = minus( B , A);
v2 = minus( P , A);
// Compute dot products
dot00 = dot(v0, v0);
dot01 = dot(v0, v1);
dot02 = dot(v0, v2);
dot11 = dot(v1, v1);
dot12 = dot(v1, v2);
// Compute barycentric coordinates
invDenom = 1 / (dot00 * dot11 - dot01 * dot01);
u = (dot11 * dot02 - dot01 * dot12) * invDenom;
v = (dot00 * dot12 - dot01 * dot02) * invDenom;
rtn.s = u;
rtn.t = v;
//rtn.Show();
return rtn;
}
// we compute the triangle coordonates from the texture coords (core of the tessation method)
BaseTriangle2.prototype.ComputeArea = function ()
{
var A = this.ST0;
var B = this.ST1;
var C = this.ST2;
var rtn;
rtn = 0.5*Math.abs((A.s-C.s)*(B.t-A.t)-(A.s-B.s)*(C.t-A.t));
return rtn;
}
// 0 outside
// 1 inside
// 2 inside close to 1 or 2 or 3 edge
BaseTriangle2.prototype.DecimateVerticesOutsideMainTriangle2 = function (x,y)
{
var uv = this.ComputeTriangleCoords(x,y);
var u = uv.s;
var v = uv.t;
var EPS = 0.01;
// Check if point is in triangle
var test = (u >= 0) && (v >= 0) && (u + v < 1);
// we add a restriction when getting close to border
if ( (test)&& ( (Math.abs((u+v)-1))<EPS ))
{
//console.log('QUITE CLOSE TO BORDER 3 u='+u+' v='+v);
return 2;
}
if ( (test)&& ( Math.abs(v)<EPS ) )
{
//console.log('QUITE CLOSE TO BORDER 2 u='+u+' v='+v);
return 2;
}
if ( (test)&& ( Math.abs(u)<EPS ) )
{
//console.log('QUITE CLOSE TO BORDER 1 u='+u+' v='+v);
return 2;
}
if (test)
return 1;
else
return 0;
//return test;
}
BaseTriangle2.prototype.FetchTextCoord = function (Id)
{
Vtx = new Vertex(this.Model.TexCoords[2*Id],this.Model.TexCoords[2*Id+1]);
return Vtx;
}
BaseTriangle2.prototype.FetchPosition = function (Id)
{
Vtx = new Vertex3D(this.Model.Vertices[3*Id],this.Model.Vertices[3*Id+1],this.Model.Vertices[3*Id+2]);
return Vtx;
}
BaseTriangle2.prototype.FetchNormal = function (Id)
{
Vtx = new Vertex3D(this.Model.Normals[3*Id],this.Model.Normals[3*Id+1],this.Model.Normals[3*Id+2]);
return Vtx;
}
BaseTriangle2.prototype.FetchTangent = function (Id)
{
Vtx = new Vertex4D(this.Model.Tangents[4*Id],this.Model.Tangents[4*Id+1],this.Model.Tangents[4*Id+2],this.Model.Tangents[4*Id+3]);
return Vtx;
}
// the vertices is the list of points's texture coordindates
// 2D vertex list extrated from the algo based on normal map
BaseTriangle2.prototype.Tessellate = function(myVertices)
{
// We know the the first triangle is the base triangle
// for debug
var LstTriangle2D = [];
// for debug we add the Base triangle
// for debug we add 1 vertex in the center
/*
var myVertices = [];
myVertices.push(this.ST0);
myVertices.push(this.ST1);
myVertices.push(this.ST2);
myVertices.push(this.InterpolateTexCoord(0.5,0.5));
*/
LstTriangle2D = Triangulate( myVertices );
//ToDo : compute triangle index list based on vertices
var idx = [];
for( i in LstTriangle2D )
{
var triangle = LstTriangle2D[i];
//triangle.Show();
for(var j=0;j<myVertices.length;j++ )
{
vtx = myVertices[j];
//console.log('LookUp');
//triangle.v0.Show();
//console.log('value');
//vtx.Show();
if ( (Math.abs(triangle.v0.s-vtx.s)<EPSILON)&&(Math.abs(triangle.v0.t-vtx.t)<EPSILON)){
//console.log('Bingo @');
idx.push(j);
break;
}
}
for(var j=0;j<myVertices.length;j++ )
{
vtx = myVertices[j];
if ( (Math.abs(triangle.v1.s-vtx.s)<EPSILON)&&(Math.abs(triangle.v1.t-vtx.t)<EPSILON)){
idx.push(j);
break;
}
}
for(var j=0;j<myVertices.length;j++ )
{
vtx = myVertices[j];
if ( (Math.abs(triangle.v2.s-vtx.s)<EPSILON)&&(Math.abs(triangle.v2.t-vtx.t)<EPSILON)){
idx.push(j);
break;
}
}
}
//console.log('IndexList is :'+idx)
return idx;
}
// interpolate values
BaseTriangle2.prototype.InterpolateTexCoord= function (u,v)
{
rtn = new Vertex(
this.ST0.s+u*(this.ST2.s-this.ST0.s)+v*(this.ST1.s-this.ST0.s),
this.ST0.t+u*(this.ST2.t-this.ST0.t)+v*(this.ST1.t-this.ST0.t)
);
return rtn;
}
BaseTriangle2.prototype.InterpolatePosition = function (u,v)
{
rtn = new Vertex3D(
this.P0.x+u*(this.P2.x-this.P0.x)+v*(this.P1.x-this.P0.x),
this.P0.y+u*(this.P2.y-this.P0.y)+v*(this.P1.y-this.P0.y),
this.P0.z+u*(this.P2.z-this.P0.z)+v*(this.P1.z-this.P0.z)
);
return rtn;
}
BaseTriangle2.prototype.InterpolateNormal = function (u,v)
{
rtn = new Vertex3D(
this.N0.x+u*(this.N2.x-this.N0.x)+v*(this.N1.x-this.N0.x),
this.N0.y+u*(this.N2.y-this.N0.y)+v*(this.N1.y-this.N0.y),
this.N0.z+u*(this.N2.z-this.N0.z)+v*(this.N1.z-this.N0.z)
);
return rtn;
}
BaseTriangle2.prototype.InterpolateTangent = function (u,v)
{
rtn = new Vertex4D(
this.T0.x+u*(this.T2.x-this.T0.x)+v*(this.T1.x-this.T0.x),
this.T0.y+u*(this.T2.y-this.T0.y)+v*(this.T1.y-this.T0.y),
this.T0.z+u*(this.T2.z-this.T0.z)+v*(this.T1.z-this.T0.z),
this.T0.w+u*(this.T2.w-this.T0.w)+v*(this.T1.w-this.T0.w)
);
return rtn;
}
//
/* Validated with JSLint, http://www.jslint.com */
////////////////////////////////////////////////////////////////////////////////
//
// Delaunay Triangulation Code, by Joshua Bell
//
// Inspired by: http://www.codeguru.com/cpp/data/mfc_database/misc/article.php/c8901/
//
// This work is hereby released into the Public Domain. To view a copy of the public
// domain dedication, visit http://creativecommons.org/licenses/publicdomain/ or send
// a letter to Creative Commons, 171 Second Street, Suite 300, San Francisco,
// California, 94105, USA.
//
////////////////////////////////////////////////////////////////////////////////
var EPSILON = 1.0e-6;
var EPSILON2 = 1.0e-12;
//------------------------------------------------------------
// BaseTriangle class
// the indexes are related to vertex property
// list of the Model Golbals
// ModelVertices = [];
// ModelNormals = [];
// ModelTangents = [];
// ModelTexCoords = [];
// ModelTriIndex = [];
//------------------------------------------------------------
function BaseTriangle(FaceIndex)
{
this.FaceIndex = FaceIndex;
this.I0 = ModelTriIndex[FaceIndex*3];
this.I1 = ModelTriIndex[FaceIndex*3+1];
this.I2 = ModelTriIndex[FaceIndex*3+2];
// Vertex
this.ST0 = this.FetchTextCoord(this.I0);
this.ST1 = this.FetchTextCoord(this.I1);
this.ST2 = this.FetchTextCoord(this.I2);
// Vertex3D
this.P0 = this.FetchPosition(this.I0);
this.P1 = this.FetchPosition(this.I1);
this.P2 = this.FetchPosition(this.I2);
// Vertex3D
this.N0 = this.FetchNormal(this.I0);
this.N1 = this.FetchNormal(this.I1);
this.N2 = this.FetchNormal(this.I2);
// Vertex4D
this.T0 = this.FetchTangent(this.I0);
this.T1 = this.FetchTangent(this.I1);
this.T2 = this.FetchTangent(this.I2);
this.AttachedLst = [];
}
BaseTriangle.prototype.Show = function()
{
// Vertex
console.log('TexCoords');
this.ST0.Show();
this.ST1.Show();
this.ST2.Show();
// Vertex3D
console.log('Positions');
this.P0.Show();
this.P1.Show();
this.P2.Show();
// Vertex3D
console.log('Normals');
this.N0.Show();
this.N1.Show();
this.N2.Show();
// Vertex3D
console.log('Tangents');
this.T0.Show();
this.T1.Show();
this.T2.Show();
}
BaseTriangle.prototype.FetchTextCoord = function (Id)
{
Vtx = new Vertex(ModelTexCoords[2*Id],ModelTexCoords[2*Id+1]);
return Vtx;
}
BaseTriangle.prototype.FetchPosition = function (Id)
{
Vtx = new Vertex3D(ModelVertices[3*Id],ModelVertices[3*Id+1],ModelVertices[3*Id+2]);
return Vtx;
}
BaseTriangle.prototype.FetchNormal = function (Id)
{
Vtx = new Vertex3D(ModelNormals[3*Id],ModelNormals[3*Id+1],ModelNormals[3*Id+2]);
return Vtx;
}
BaseTriangle.prototype.FetchTangent = function (Id)
{
Vtx = new Vertex4D(ModelTangents[4*Id],ModelTangents[4*Id+1],ModelTangents[4*Id+2],ModelTangents[4*Id+3]);
return Vtx;
}
// we compute the triangle coordonates from the texture coords (core of the tessation method)
BaseTriangle.prototype.ComputeArea = function ()
{
var A = this.ST0;
var B = this.ST1;
var C = this.ST2;
var rtn;
rtn = 0.5*Math.abs((A.s-C.s)*(B.t-A.t)-(A.s-B.s)*(C.t-A.t));
return rtn;
}
// we compute the triangle coordonates from the texture coords (core of the tessation method)
BaseTriangle.prototype.ComputeTriangleCoords = function (s,t)
{
var rtn = new Vertex(0,0);
var A = this.ST0;
var B = this.ST1;
var C = this.ST2;
//rejection test:
var sMax = Math.max(A.s,Math.max(B.s,C.s));
var tMax = Math.max(A.t,Math.max(B.t,C.t));
var sMin = Math.min(A.s,Math.min(B.s,C.s));
var tMin = Math.min(A.t,Math.min(B.t,C.t));
/*
console.log('xMax:'+xMax);
console.log('xMin:'+xMin);
console.log('yMax:'+yMax);
console.log('yMin:'+yMin);
*/
/*
if ( (s>sMin) && (s<sMax) && ( t>tMin) && (t<tMax) ){
//nothing
}else{
return false;
}
*/
var P = new Vertex(s,t);
function dot( vA, vB){
return (vA.s*vB.s+vA.t*vB.t);
}
function minus( vA, vB){
return new Vertex(vA.s-vB.s,vA.t-vB.t);
}
// Compute vectors
v0 = minus( C , A);
v1 = minus( B , A);
v2 = minus( P , A);
// Compute dot products
dot00 = dot(v0, v0);
dot01 = dot(v0, v1);
dot02 = dot(v0, v2);
dot11 = dot(v1, v1);
dot12 = dot(v1, v2);
// Compute barycentric coordinates
invDenom = 1 / (dot00 * dot11 - dot01 * dot01);
u = (dot11 * dot02 - dot01 * dot12) * invDenom;
v = (dot00 * dot12 - dot01 * dot02) * invDenom;
rtn.s = u;
rtn.t = v;
//rtn.Show();
return rtn;
}
//will return a list of vertices based on the image processing
BaseTriangle.prototype.createVerticesFromNormalMap = function(data,H,W){
var rtn = []
// quickly iterate over all pixels
var cpt = 0;
var discardRate = 0;
var discardRateM = 0;//32
var IsInside = new Boolean();
// get the base triangle info
height = H;
width = W;
var MaxRho2 = -1;
for(var i = 0, n = data.length; i < n; i += 4) {
var red = data[i];
var green = data[i + 1];
//var blue = data[i + 2];
//var alpha = data[i + 3];
var threshold = 0.0001;
var y = Math.floor((i/4)/height);
var x = (i/4)-y*width;
//console.log('===x'+x+' ===y'+y);
function computeVoisin(xn,yn){
var dx = 0;
var dy = 0;
var xp1 =0;
var yp1 = 0;
var xn1 =0;
var yn1 = 0;
if (x<(W-1)){
var xp1 = x+1;
}
if (y<(H-1)){
var yp1 = y+1;
}
if (x>1){
var xn1 = x-1;
}
if (y>1){
var yn1 = y-1;
}
var ii = 4*((yp1*width)+xp1);
var redP1 = data[ii];
var greenP1 =data[ii+1];
//console.log('ii='+ii);
//console.log('red='+red+' red1='+redP1);
//console.log('green='+green+' greenP1='+greenP1)
var XPn = 2*(redP1/255)-1.0;
var YPn = 2*(greenP1/255)-1.0;
//-- Other voisin
var iii = 4*((yn1*width)+xn1);
var redN1 = data[iii];
var greenN1 =data[iii+1];
//console.log('ii='+ii);
//console.log('red='+red+' red1='+redP1);
//console.log('green='+green+' greenP1='+greenP1)
var XNn = 2*(redN1/255)-1.0;
var YNn = 2*(greenN1/255)-1.0;
dx = XPn-XNn;
dy = YPn-YNn;
return (dx*dx + dy*dy);
//return (Xn*Xn + Yn*Yn);
}
var s = x/height;
var t = y/width;
//IsInside = this.DecimateVerticesOutsideMainTriangle(s,t);
var IsInsideVal = this.DecimateVerticesOutsideMainTriangle2(s,t);
//IsInside = true;
if (IsInsideVal==1){
//console.log('===x'+x+' ===y'+y);
//console.log('====['+i+']='+'r:'+red+'g:'+green+'b:'+blue);
var Xn = 2*(red/255)-1.0;
var Yn = 2*(green/255)-1.0;
//var Zn = (blue-127)/127;
//console.log('====['+i+']='+' Zn:'+Zn);
//if ( (Math.abs(Xn)>threshold) && (Math.abs(Yn)>threshold) ){
//if ( (Math.abs(Xn)>threshold) || (Math.abs(Yn)>threshold) ){
var Rho2 = Xn*Xn + Yn*Yn;
///discardRate = discardRateM*(Rho2)*(Rho2);
MaxRho2 = Math.max(MaxRho2,Rho2);
//console.log('MaxRho2:'+MaxRho2);
//if ( Rho2 > 0.051 ){
// 0.2
////var Rho2V = computeVoisin(Xn,Yn);
///var diffRho2 = Math.abs(Rho2V-Rho2);
var diffRho2 = computeVoisin(Xn,Yn);
//console.log('diffRho2:'+diffRho2);
//if ( Rho2 > 0.2001){
if (diffRho2 > 0.01){// good 0.35
//if (diffRho2 > 0.4){// good 0.35 (other method)
//console.log('===x'+x+' ===y'+y);
//if ( (cpt>discardRate) || (Rho2>0.8) )
if ( cpt>discardRate)
{
rtn.push(new Vertex(s,t));
cpt=0;
}
}
}else if (IsInsideVal==2) {
// This is solving the cracks problem but with quite a high price of extra polys
//console.log('special');
rtn.push(new Vertex(s,t));
}
cpt++;
}
return rtn;
}
//------------------------------------------------------------------------------
// -- old
//will return a list of vertices based on the image processing
BaseTriangle.prototype.createVerticesFromNormalMapOld = function(data,H,W){
var rtn = []
// quickly iterate over all pixels
var cpt = 0;
var discardRate = 0;
var discardRateM = 2;//32
var IsInside = new Boolean();
// get the base triangle info
height = H;
width = W;
var MaxRho2 = -1;
for(var i = 0, n = data.length; i < n; i += 4) {
var red = data[i];
var green = data[i + 1];
//var blue = data[i + 2];
//var alpha = data[i + 3];
var threshold = 0.0001;
var y = Math.floor((i/4)/height);
var x = (i/4)-y*width;
//console.log('===x'+x+' ===y'+y);
var s = x/height;
var t = y/width;
IsInside = this.DecimateVerticesOutsideMainTriangle(s,t);
//IsInside = true;
if (IsInside){
//console.log('===x'+x+' ===y'+y);
//console.log('====['+i+']='+'r:'+red+'g:'+green+'b:'+blue);
var Xn = 2*(red/255)-1.0;
var Yn = 2*(green/255)-1.0;
//var Zn = (blue-127)/127;
//console.log('====['+i+']='+' Zn:'+Zn);
//if ( (Math.abs(Xn)>threshold) && (Math.abs(Yn)>threshold) ){
//if ( (Math.abs(Xn)>threshold) || (Math.abs(Yn)>threshold) ){
var Rho2 = Xn*Xn + Yn*Yn;
discardRate = discardRateM*(Rho2);
MaxRho2 = Math.max(MaxRho2,Rho2);
//console.log('MaxRho2:'+MaxRho2);
//if ( Rho2 > 0.051 ){
// 0.2
if ( Rho2 > 0.0001){
//console.log('===x'+x+' ===y'+y);
//if ( (cpt>discardRate) || (Rho2>0.8) )
if ( cpt>discardRate)
{
rtn.push(new Vertex(s,t));
cpt=0;
}
}
}
cpt++;
}
return rtn;
}
// interpolate values
BaseTriangle.prototype.InterpolateTexCoord= function (u,v)
{
rtn = new Vertex(
this.ST0.s+u*(this.ST2.s-this.ST0.s)+v*(this.ST1.s-this.ST0.s),
this.ST0.t+u*(this.ST2.t-this.ST0.t)+v*(this.ST1.t-this.ST0.t)
);
return rtn;
}
BaseTriangle.prototype.InterpolatePosition = function (u,v)
{
rtn = new Vertex3D(
this.P0.x+u*(this.P2.x-this.P0.x)+v*(this.P1.x-this.P0.x),
this.P0.y+u*(this.P2.y-this.P0.y)+v*(this.P1.y-this.P0.y),
this.P0.z+u*(this.P2.z-this.P0.z)+v*(this.P1.z-this.P0.z)
);
return rtn;
}
BaseTriangle.prototype.InterpolateNormal = function (u,v)
{
rtn = new Vertex3D(
this.N0.x+u*(this.N2.x-this.N0.x)+v*(this.N1.x-this.N0.x),
this.N0.y+u*(this.N2.y-this.N0.y)+v*(this.N1.y-this.N0.y),
this.N0.z+u*(this.N2.z-this.N0.z)+v*(this.N1.z-this.N0.z)
);
return rtn;
}
BaseTriangle.prototype.InterpolateTangent = function (u,v)
{
rtn = new Vertex4D(
this.T0.x+u*(this.T2.x-this.T0.x)+v*(this.T1.x-this.T0.x),
this.T0.y+u*(this.T2.y-this.T0.y)+v*(this.T1.y-this.T0.y),
this.T0.z+u*(this.T2.z-this.T0.z)+v*(this.T1.z-this.T0.z),
this.T0.w+u*(this.T2.w-this.T0.w)+v*(this.T1.w-this.T0.w)
);
return rtn;
}
BaseTriangle.prototype.DecimateVerticesOutsideMainTriangle = function (x,y)
{
var uv = this.ComputeTriangleCoords(x,y);
var u = uv.s;
var v = uv.t;
// we add a restriction when getting close to border
// Check if point is in triangle
return (u >= 0) && (v >= 0) && (u + v < 1)
}
// 0 outside
// 1 inside
// 2 inside close to 1 or 2 or 3 edge
BaseTriangle.prototype.DecimateVerticesOutsideMainTriangle2 = function (x,y)
{
var uv = this.ComputeTriangleCoords(x,y);
var u = uv.s;
var v = uv.t;
var EPS = 0.01;
// Check if point is in triangle
var test = (u >= 0) && (v >= 0) && (u + v < 1);
// we add a restriction when getting close to border
if ( (test)&& ( (Math.abs((u+v)-1))<EPS ))
{
//console.log('QUITE CLOSE TO BORDER 3 u='+u+' v='+v);
return 2;
}
if ( (test)&& ( Math.abs(v)<EPS ) )
{
//console.log('QUITE CLOSE TO BORDER 2 u='+u+' v='+v);
return 2;
}
if ( (test)&& ( Math.abs(u)<EPS ) )
{
//console.log('QUITE CLOSE TO BORDER 1 u='+u+' v='+v);
return 2;
}
if (test)
return 1;
else
return 0;
//return test;
}
// the vertices is the list of points's texture coordindates
// 2D vertex list extrated from the algo based on normal map
BaseTriangle.prototype.Tessellate = function(myVertices)
{
// We know the the first triangle is the base triangle
// for debug
var LstTriangle2D = [];
// for debug we add the Base triangle
// for debug we add 1 vertex in the center
/*
var myVertices = [];
myVertices.push(this.ST0);
myVertices.push(this.ST1);
myVertices.push(this.ST2);
myVertices.push(this.InterpolateTexCoord(0.5,0.5));
*/
LstTriangle2D = Triangulate( myVertices );
//ToDo : compute triangle index list based on vertices
var idx = [];
for( i in LstTriangle2D )
{
var triangle = LstTriangle2D[i];
//triangle.Show();
for(var j=0;j<myVertices.length;j++ )
{
vtx = myVertices[j];
//console.log('LookUp');
//triangle.v0.Show();
//console.log('value');
//vtx.Show();
if ( (Math.abs(triangle.v0.s-vtx.s)<EPSILON)&&(Math.abs(triangle.v0.t-vtx.t)<EPSILON)){
//console.log('Bingo @');
idx.push(j);
break;
}
}
for(var j=0;j<myVertices.length;j++ )
{
vtx = myVertices[j];
if ( (Math.abs(triangle.v1.s-vtx.s)<EPSILON)&&(Math.abs(triangle.v1.t-vtx.t)<EPSILON)){
idx.push(j);
break;
}
}
for(var j=0;j<myVertices.length;j++ )
{
vtx = myVertices[j];
if ( (Math.abs(triangle.v2.s-vtx.s)<EPSILON)&&(Math.abs(triangle.v2.t-vtx.t)<EPSILON)){
idx.push(j);
break;
}
}
}
//console.log('IndexList is :'+idx)
return idx;
}
function Vertex4D( x, y, z, w )
{
this.x = x;
this.y = y;
this.z = z;
this.w = w;
} // Vertex4D
Vertex4D.prototype.Show = function()
{
console.log('--- Verbose ---');
console.log('= x: '+this.x);
console.log('= y: '+this.y);
console.log('= z: '+this.z);
console.log('= w: '+this.w);
}
function Vertex3D( x, y, z )
{
this.x = x;
this.y = y;
this.z = z;
} // Vertex3D
Vertex3D.prototype.Show = function()
{
console.log('--- Verbose ---');
console.log('= x: '+this.x);
console.log('= y: '+this.y);
console.log('= z: '+this.z);
}
Vertex3D.prototype.Normalize= function()
{
var a = this.x;
var b = this.y;
var c = this.z;
var sq = a*a+b*b+c*c;
/*
if ( sq <EPSILON2 ){
alert("normal TOO SMALL");
}else
*/
{
invnorm = 1/Math.sqrt(sq);
this.x = a*invnorm;
this.y = b*invnorm;
this.z = c*invnorm;
}
}
//------------------------------------------------------------
// Vertex class
//------------------------------------------------------------
function Vertex( s, t )
{
this.s = s;
this.t = t;
} // Vertex
/*
Vertex.prototype.dot( var Vertex vA, var Vertex vB){
return (vA.x*vB.x+vA.t*vB.t);
}
*/
Vertex.prototype.Show = function()
{
console.log('--- Verbose ---');
console.log('= s: '+this.s);
console.log('= t: '+this.t);
}
//------------------------------------------------------------
// Triangle class (2D)
//------------------------------------------------------------
function Triangle( v0, v1, v2 )
{
this.v0 = v0;
this.v1 = v1;
this.v2 = v2;
this.CalcCircumcircle();
} // Triangle
Triangle.prototype.Show = function()
{
console.log('Triangle');
this.v0.Show();
this.v1.Show();
this.v2.Show();
}
Triangle.prototype.CalcCircumcircle = function()
{
// From: http://www.exaflop.org/docs/cgafaq/cga1.html
var A = this.v1.s - this.v0.s;
var B = this.v1.t - this.v0.t;
var C = this.v2.s - this.v0.s;
var D = this.v2.t - this.v0.t;
var E = A*(this.v0.s + this.v1.s) + B*(this.v0.t + this.v1.t);
var F = C*(this.v0.s + this.v2.s) + D*(this.v0.t + this.v2.t);
var G = 2.0*(A*(this.v2.t - this.v1.t)-B*(this.v2.s - this.v1.s));
var ds, dt;
if( Math.abs(G) < EPSILON )
{
// Collinear - find extremes and use the midpoint
function max3( a, b, c ) { return ( a >= b && a >= c ) ? a : ( b >= a && b >= c ) ? b : c; }
function min3( a, b, c ) { return ( a <= b && a <= c ) ? a : ( b <= a && b <= c ) ? b : c; }
var mins = min3( this.v0.s, this.v1.s, this.v2.s );
var mint = min3( this.v0.t, this.v1.t, this.v2.t );
var maxs = max3( this.v0.s, this.v1.s, this.v2.s );
var maxt = max3( this.v0.t, this.v1.t, this.v2.t );
this.center = new Vertex( ( mins + maxs ) / 2, ( mint + maxt ) / 2 );
ds = this.center.s - mins;
dt = this.center.t - mint;
}
else
{
var cs = (D*E - B*F) / G;
var ct = (A*F - C*E) / G;
this.center = new Vertex( cs, ct );
ds = this.center.s - this.v0.s;
dt = this.center.t - this.v0.t;
}
this.radius_squared = ds * ds + dt * dt;
this.radius = Math.sqrt( this.radius_squared );
}; // CalcCircumcircle
Triangle.prototype.InCircumcircle = function( v )
{
var ds = this.center.s - v.s;
var dt = this.center.t - v.t;
var dist_squared = ds * ds + dt * dt;
return ( dist_squared <= this.radius_squared );
}; // InCircumcircle
//------------------------------------------------------------
// Edge class
//------------------------------------------------------------
function Edge( v0, v1 )
{
this.v0 = v0;
this.v1 = v1;
} // Edge
//------------------------------------------------------------
// Triangulate2
//
// Perform the Delaunay Triangulation of a set of vertices.
//
// vertices: Array of Vertex objects
//
// returns: Array of Index
//------------------------------------------------------------
// the first 3 vertices are the model triangles
function Triangulate2( vertices )
{
var Tri = Triangulate(vertices);
}
//------------------------------------------------------------
// Triangulate
//
// Perform the Delaunay Triangulation of a set of vertices.
//
// vertices: Array of Vertex objects
//
// returns: Array of Triangles
//------------------------------------------------------------
function Triangulate( vertices )
{
var triangles = [];
//
// First, create a "supertriangle" that bounds all vertices
//
var st = CreateBoundingTriangle( vertices );
triangles.push( st );
//
// Next, begin the triangulation one vertex at a time
//
var i;
for( i in vertices )
{
// NOTE: This is O(n^2) - can be optimized by sorting vertices
// along the x-axis and only considering triangles that have
// potentially overlapping circumcircles
var vertex = vertices[i];
AddVertex( vertex, triangles );
}
//
// Remove triangles that shared edges with "supertriangle"
//
for( i in triangles )
{
var triangle = triangles[i];
if( triangle.v0 == st.v0 || triangle.v0 == st.v1 || triangle.v0 == st.v2 ||
triangle.v1 == st.v0 || triangle.v1 == st.v1 || triangle.v1 == st.v2 ||
triangle.v2 == st.v0 || triangle.v2 == st.v1 || triangle.v2 == st.v2 )
{
delete triangles[i];
}
}
// loop up to provide an indexed triangle list
return triangles;
} // Triangulate
// Internal: create a triangle that bounds the given vertices, with room to spare
function CreateBoundingTriangle( vertices )
{
// NOTE: There's a bit of a heuristic here. If the bounding triangle
// is too large and you see overflow/underflow errors. If it is too small
// you end up with a non-convex hull.
var mins, mint, maxs, maxt;
for( var i in vertices )
{
var vertex = vertices[i];
if( mins === undefined || vertex.s < mins ) { mins = vertex.s; }
if( mint === undefined || vertex.t < mint ) { mint = vertex.t; }
if( maxs === undefined || vertex.s > maxs ) { maxs = vertex.s; }
if( maxt === undefined || vertex.t > maxt ) { maxt = vertex.t; }
}
var ds = ( maxs - mins ) * 10;
var dt = ( maxt - mint ) * 10;
var stv0 = new Vertex( mins - ds, mint - dt*3 );
var stv1 = new Vertex( mins - ds, maxt + dt );
var stv2 = new Vertex( maxs + ds*3, maxt + dt );
return new Triangle( stv0, stv1, stv2 );
} // CreateBoundingTriangle
// Internal: update triangulation with a vertex
function AddVertex( vertex, triangles )
{
var edges = [];
// Remove triangles with circumcircles containing the vertex
var i;
for( i in triangles )
{
var triangle = triangles[i];
if( triangle.InCircumcircle( vertex ) )
{
edges.push( new Edge( triangle.v0, triangle.v1 ) );
edges.push( new Edge( triangle.v1, triangle.v2 ) );
edges.push( new Edge( triangle.v2, triangle.v0 ) );
delete triangles[i];
}
}
edges = UniqueEdges( edges );
// Create new triangles from the unique edges and new vertex
for( i in edges )
{
var edge = edges[i];
triangles.push( new Triangle( edge.v0, edge.v1, vertex ) );
}
} // AddVertex
// Internal: remove duplicate edges from an array
function UniqueEdges( edges )
{
// TODO: This is O(n^2), make it O(n) with a hash or some such
var uniqueEdges = [];
for( var i in edges )
{
var edge1 = edges[i];
var unique = true;
for( var j in edges )
{
if( i != j )
{
var edge2 = edges[j];
if( ( edge1.v0 == edge2.v0 && edge1.v1 == edge2.v1 ) ||
( edge1.v0 == edge2.v1 && edge1.v1 == edge2.v0 ) )
{
unique = false;
break;
}
}
}
if( unique )
{
uniqueEdges.push( edge1 );
}
}
return uniqueEdges;
} // UniqueEdges
댓글
댓글 쓰기