我有一个Java项目,用射线投射算法生成“迷宫”,以下是一张截图:
正如你所看到的,所有的墙壁高度都相同。我希望相同的射线投射算法可以产生不同高度的效果。
private void castRay(int xOnScreen,double angle,double direction) {
R rx = castRayInX(angle,direction);
R ry = castRayInY(angle,direction);
// In case of out-of-space rays
if (rx.getDistance()==Double.MAX_VALUE && ry.getDistance()==Double.MAX_VALUE) {
graphics.setColor(BACKGROUND);
graphics.drawLine(xOnScreen,0,xOnScreen,this.image.getHeight());
return;
}
double distance = rx.getDistance();
double normal = rx.getNormal();
Color c = rx.getColor();
double coef = Math.cos((angle+direction+Math.PI)-normal);
Plot collision = rx.getPlot();
if (ry.getDistance()<rx.getDistance()) {
distance = ry.getDistance();
normal = ry.getNormal();
c = ry.getColor();
coef = Math.cos((angle+direction+Math.PI)-normal);
collision = ry.getPlot();
}
coef = Math.abs(coef);
int factor = map.length*SQUARE_SIZE;
double d = (double)(distance+factor)/factor;
coef *= 1/(d*d);
Color c2 = new Color((int)(c.getRed()*coef),(int)(c.getGreen()*coef),(int)(c.getBlue()*coef));
graphics.setColor(c2);
// graphics.setColor(c); // no illumination
distance *= Math.cos(angle); // lens correction
int h = (int)(this.screenDistance/distance*WALL_HEIGHT); // perspective height
int vh = this.image.getHeight();
graphics.drawLine(xOnScreen,(vh-h)/2,xOnScreen,(vh+h)/2);
drawEye(direction,collision);
}
private R castRayInX(double angleRay,double direction) {
double angle = angleRay+direction;
double x1 = eye.getX()+SQUARE_SIZE*Math.cos(angle);
double y1 = eye.getY()+SQUARE_SIZE*Math.sin(angle);
double slope = (y1-eye.getY())/(x1-eye.getX());
if (Math.cos(angle)==0) {
if (Math.sin(angle)>0)
return new R(Double.MAX_VALUE,3*Math.PI/2,BACKGROUND,null);
else
return new R(Double.MAX_VALUE,Math.PI/2,BACKGROUND,null);
}
if (Math.cos(angle)>0) {
int firstX = ((eye.getX()/SQUARE_SIZE)+1)*SQUARE_SIZE;
R r = new R(Double.MAX_VALUE,angle+Math.PI,BACKGROUND,null);
for (int x = firstX; x<map[0].length*SQUARE_SIZE; x += SQUARE_SIZE) {
int y = (int)(slope*(x-eye.getX())+eye.getY());
if (isOutside(x,y,Color.MAGENTA,this.showRayCastingX)) break;
Color c = colorAt(x,y);
if (c==null) c = colorAt(x,y-1);
if (c==null) c = colorAt(x-1,y);
if (c==null) c = colorAt(x-1,y-1);
if (c!=null) {
int DX = x-eye.getX();
double DY = y-eye.getY();
return new R(Math.sqrt(DX*DX+DY*DY),Math.PI,c,new Plot((int)x,(int)y, WALL_HEIGHT));
}
}
return r;
} else {
int firstX = ((eye.getX()/SQUARE_SIZE))*SQUARE_SIZE;
R r = new R(Double.MAX_VALUE,angle+Math.PI,BACKGROUND,null);
for (int x = firstX; x>=0; x -= SQUARE_SIZE) {
int y = (int)(slope*(x-eye.getX())+eye.getY());
if (isOutside(x,y,Color.MAGENTA,this.showRayCastingX)) break;
Color c = colorAt(x,y);
if (c==null) c = colorAt(x,y-1);
if (c==null) c = colorAt(x-1,y);
if (c==null) c = colorAt(x-1,y-1);
if (c!=null) {
int DX = x-eye.getX();
double DY = y-eye.getY();
return new R(Math.sqrt(DX*DX+DY*DY),0,c,new Plot((int)x,(int)y, WALL_HEIGHT));
}
}
return r;
}
}
private R castRayInY(double angleRay,double direction) {
// System.out.println("cast ray 2 Y "+angleRay+" "+direction);
double angle = angleRay+direction;
double x1 = eye.getX()+SQUARE_SIZE*Math.cos(angle);
double y1 = eye.getY()+SQUARE_SIZE*Math.sin(angle);
// System.out.println(eye+" "+x1+" "+y1);
double slope = (y1-eye.getY())/(x1-eye.getX());
if (Math.sin(angle)==0) {
if (Math.cos(angle)>0)
return new R(Double.MAX_VALUE,Math.PI,BACKGROUND,null);
else
return new R(Double.MAX_VALUE,0,BACKGROUND,null);
}
if (Math.sin(angle)>0) {
int firstY = ((eye.getY()/SQUARE_SIZE)+1)*SQUARE_SIZE;
R r = new R(Double.MAX_VALUE,angle+Math.PI,BACKGROUND,null);
for (int y = firstY; y<map.length*SQUARE_SIZE; y += SQUARE_SIZE) {
int x = (int)((y-eye.getY())/slope)+eye.getX();
if (isOutside(x,y,Color.CYAN,this.showRayCastingY)) break;
Color c = colorAt(x,y);
if (c==null) c = colorAt(x,y-1);
if (c==null) c = colorAt(x-1,y);
if (c==null) c = colorAt(x-1,y-1);
if (c!=null) {
double DX = x-eye.getX();
int DY = y-eye.getY();
return new R(Math.sqrt(DX*DX+DY*DY),3*Math.PI/2,c,new Plot((int)x,(int)y, WALL_HEIGHT));
}
}
return r;
} else {
int firstY = ((eye.getY()/SQUARE_SIZE))*SQUARE_SIZE;
R r = new R(Double.MAX_VALUE,angle+Math.PI,BACKGROUND,null);
for (int y = firstY; y>=0; y -= SQUARE_SIZE) {
int x = (int)((y-eye.getY())/slope)+eye.getX();
if (isOutside(x,y,Color.CYAN,this.showRayCastingY)) break;
Color c = colorAt(x,y);
if (c==null) c = colorAt(x,y-1);
if (c==null) c = colorAt(x-1,y);
if (c==null) c = colorAt(x-1,y-1);
if (c!=null) {
double DX = x-eye.getX();
int DY = y-eye.getY();
return new R(Math.sqrt(DX*DX+DY*DY),Math.PI/2,c,new Plot((int)x,(int)y, WALL_HEIGHT));
}
}
return r;
}
}
我的R
类现在有一个Plot (x, y, z)
,我现在使用WALL_HEIGHT
作为颜色、距离和光线的法线。目前这样做是有效的,但我想添加一个新的函数,比如castRayInZ,但我没有背后的所有数学理论知识。我的迷宫是由以下地图组成:
private String [][]map = { // each: SQUARE_SIZE x SQUARE_SIZE
{ "Y300", "Z500", "X230", "Y112", "Z321", "X120", "X354" },
{ "X89", " ", " ", " ", "Y120", " ", "X232" },
{ "Z124", " ", "X276", " ", "X123", " ", "X" },
{ "Y290", " ", " ", " ", " ", " ", "X100" },
{ "X32", "Z430", " ", "Y500", "X120", " ", "X123" },
{ "X222", " ", " ", " ", " ", " ", "X210" },
{ "X12", "Y98", "Y763", "X146", "Y111", "Y333", "X321" }
这里的X Y Z代表颜色(X代表红色,Y代表绿色,Z代表蓝色,只是测试我的光函数),我为地图上的每个方块添加了高度。目前我将所有长度都设置为SQUARE_LENGTH
,也许以后我会将每个方块的大小减小到一个像素,并通过生成来放大我的地图。但我真的想知道如何改变每个方块的高度。我已经在这上面工作了4天了,但还没有任何线索...
编辑
我有一些消息,我改变了墙壁的大小,但出现了一些奇怪的东西,下面是截图:
正如您所看到的,这里出现了一些奇怪的东西。以下是我的代码:
private void castRay(int xOnScreen,double angle,double direction) {
R rx = castRayInX(angle,direction);
R ry = castRayInY(angle,direction);
// In case of out-of-space rays
if (rx.getDistance()==Double.MAX_VALUE && ry.getDistance()==Double.MAX_VALUE) {
graphics.setColor(BACKGROUND);
graphics.drawLine(xOnScreen,0,xOnScreen,this.image.getHeight());
return;
}
double distance = rx.getDistance();
double normal = rx.getNormal();
Color c = rx.getColor();
double coef = Math.cos((angle+direction+Math.PI)-normal);
Plot collision = rx.getPlot();
if (ry.getDistance()<rx.getDistance()) {
distance = ry.getDistance();
normal = ry.getNormal();
c = ry.getColor();
coef = Math.cos((angle+direction+Math.PI)-normal);
collision = ry.getPlot();
}
coef = Math.abs(coef);
int factor = map.length*SQUARE_SIZE;
double d = (double)(distance+factor)/factor;
coef *= 1/(d*d);
Color c2 = new Color((int)(c.getRed()*coef),(int)(c.getGreen()*coef),(int)(c.getBlue()*coef));
graphics.setColor(c);
distance *= Math.cos(angle); // lens correction
int h;
int hw = (int)(this.screenDistance/distance*WALL_HEIGHT); //WALL_HEIGHT value is 300px at default
if(rx.getPlot() != null)
h = (int)(this.screenDistance/distance*rx.getPlot().getZ()); // perspective height
else
h = (int)(this.screenDistance/distance*WALL_HEIGHT);
int vh = this.image.getHeight();
int y0 = (hw+vh)/2;
int y1 = (vh-h)/2;
graphics.drawLine(xOnScreen,y0,xOnScreen,y1);
drawEye(direction,collision);
我的问题应该来自于 castRayInX
函数:
private R castRayInX(double angleRay,double direction) {
double angle = angleRay+direction;
double x1 = eye.getX()+SQUARE_SIZE*Math.cos(angle);
double y1 = eye.getY()+SQUARE_SIZE*Math.sin(angle);
double slope = (y1-eye.getY())/(x1-eye.getX());
if (Math.cos(angle)==0) {
if (Math.sin(angle)>0)
return new R(Double.MAX_VALUE,3*Math.PI/2,BACKGROUND,null);
else
return new R(Double.MAX_VALUE,Math.PI/2,BACKGROUND,null);
}
if (Math.cos(angle)>0) {
int firstX = ((eye.getX()/SQUARE_SIZE)+1)*SQUARE_SIZE;
R r = new R(Double.MAX_VALUE,angle+Math.PI,BACKGROUND,null);
for (int x = firstX; x<map[0].length*SQUARE_SIZE; x += SQUARE_SIZE) {
int y = (int)(slope*(x-eye.getX())+eye.getY());
if (isOutside(x,y,Color.MAGENTA,this.showRayCastingX)) break;
Color c = colorAt(x,y);
int z = heightAt(x,y);
if (c==null) c = colorAt(x,y-1);
if (c==null) c = colorAt(x-1,y);
if (c==null) c = colorAt(x-1,y-1);
if (z == 0) z = heightAt(x,y-1);
if (z == 0) z = heightAt(x-1,y);
if (z == 0) z = heightAt(x-1,y-1);
if (c!=null) {
int DX = x-eye.getX();
double DY = y-eye.getY();
return new R(Math.sqrt(DX*DX+DY*DY),Math.PI,c,new Plot((int)x,(int)y,(int)z));
}
}
return r;
} else {
int firstX = ((eye.getX()/SQUARE_SIZE))*SQUARE_SIZE;
R r = new R(Double.MAX_VALUE,angle+Math.PI,BACKGROUND,null);
for (int x = firstX; x>=0; x -= SQUARE_SIZE) {
int y = (int)(slope*(x-eye.getX())+eye.getY());
if (isOutside(x,y,Color.MAGENTA,this.showRayCastingX)) break;
Color c = colorAt(x,y);
int z = heightAt(x,y);
if (c==null) c = colorAt(x,y-1);
if (c==null) c = colorAt(x-1,y);
if (c==null) c = colorAt(x-1,y-1);
if (z == 0) z = heightAt(x,y-1);
if (z == 0) z = heightAt(x-1,y);
if (z == 0) z = heightAt(x-1,y-1);
if (c!=null) {
int DX = x-eye.getX();
double DY = y-eye.getY();
return new R(Math.sqrt(DX*DX+DY*DY),0,c,new Plot((int)x,(int)y,(int)z));
}
}
return r;
}
}
我应该创建一个castRayInZ
函数吗?还是我应该从其他地方获取我的z
值?