ভক্সেল গেমটিতে ব্লক নির্বাচন করতে রশ্মি কাস্ট করুন


22

আমি ব্লকগুলি তৈরি করে মাইনক্রাফ্টের মতো অঞ্চল নিয়ে একটি গেমটি বিকাশ করছি। যেহেতু এখন বেসিক রেন্ডারিং এবং খণ্ড লোডিং সম্পন্ন হয়েছে, তাই আমি ব্লক নির্বাচন প্রয়োগ করতে চাই।

সুতরাং প্রথম ব্যক্তির ক্যামেরাটি কোন ব্লকের মুখোমুখি হচ্ছে তা আমাকে খুঁজে বের করতে হবে। আমি ইতিমধ্যে পুরো দৃশ্যটি অপ্রজেক্ট করার কথা শুনেছি তবে আমি এর বিরুদ্ধে সিদ্ধান্ত নিয়েছি কারণ এটি হ্যাকিয়া লাগছে এবং সঠিক নয়। হতে পারে আমি কোনওভাবে দেখার দিকের দিকে একটি রশ্মি ফেলতে পারি তবে আমার ভক্সেলের ডেটাতে ব্লকের সাথে সংঘর্ষটি কীভাবে পরীক্ষা করতে হয় তা আমি জানি না। গেম লজিক ক্রিয়াকলাপ সম্পাদন করার জন্য আমার ফলাফলের যেহেতু অবশ্যই এই গণনাগুলি অবশ্যই সিপিইউতে করা উচিত।

তাহলে ক্যামেরাটির সামনে কোন ব্লকটি রয়েছে তা আমি কীভাবে জানতে পারি? যদি এটি অগ্রাধিকারযোগ্য হয় তবে আমি কীভাবে একটি রশ্মি ফেলব এবং সংঘর্ষগুলি চেক করব?


আমি নিজে কখনোই করিনি। তবে আপনি কেবল ক্যামেরা বিমান থেকে একটি "রশ্মি" (এই ক্ষেত্রে লিনিসিমেট) পেতে পারেননি, একটি নির্দিষ্ট দৈর্ঘ্য সহ একটি সাধারণ ভেক্টর (আপনি কেবল এটি একটি ব্যাসার্ধের মধ্যে থাকতে চান) এবং এটি কোনওটির সাথে ছেদ করে কিনা তা দেখতে পান ব্লক। আমি অনুমান করি আংশিক ব্যবধান এবং ক্লিপিং পাশাপাশি প্রয়োগ করা হয়েছে। সুতরাং কোন ব্লক দিয়ে পরীক্ষা করতে হবে তা জানার পরে খুব বেশি সমস্যা হওয়া উচিত না ... আমার মনে হয়?
সিডার

উত্তর:


21

আমার কিউবগুলিতে কাজ করার সময় আমার যখন এই সমস্যা হয়েছিল তখন আমি জন অমানাটিডেস এবং অ্যান্ড্রু উ, ১৯৮7-র র "ট্র্যাসিংয়ের জন্য একটি ফাস্ট ভোক্সেল ট্র্যাভারসাল অ্যালগরিদম" পত্রিকাটি পেয়েছিলাম যা একটি অ্যালগরিদম বর্ণনা করে যা এই কাজটিতে প্রয়োগ করা যেতে পারে; এটি সঠিক এবং প্রতি ভক্সেল ছেদ করাতে কেবল একটি লুপ পুনরাবৃত্তি প্রয়োজন।

আমি জাভাস্ক্রিপ্টে কাগজের আলগোরিদিম সম্পর্কিত অংশগুলির একটি প্রয়োগ লিখেছি written আমার বাস্তবায়ন দুটি বৈশিষ্ট্য যুক্ত করে: এটি র‌্যাস্টের দূরত্বের একটি সীমা নির্দিষ্ট করার অনুমতি দেয় (পারফরম্যান্সের সমস্যাগুলি এড়াতে পাশাপাশি একটি সীমিত 'পৌঁছনো সংজ্ঞা দেওয়ার জন্য দরকারী), এবং প্রতিটি ভক্সেলের কোন মুখের রশ্মি প্রবেশ করেছিল তা গণনা করে।

ইনপুট originভেক্টরটি অবশ্যই এমনভাবে ছোট করতে হবে যাতে ভক্সেলের পাশের দৈর্ঘ্য 1 হয়। directionভেক্টরের দৈর্ঘ্য তাত্পর্যপূর্ণ নয় তবে এটি অ্যালগরিদমের সংখ্যাসূচক নির্ভুলতার উপর প্রভাব ফেলতে পারে।

আলগোরিদিম রশ্মির একটি প্যারামিটারাইজড উপস্থাপনা ব্যবহার করে পরিচালনা করে origin + t * direction। প্রতিটি অক্ষ তুল্য জন্য, আমরা ট্র্যাক রাখতে tমান যা আমরা হয়ে যাব যদি আমরা একটি পদক্ষেপ যে অক্ষ বরাবর একটি ভক্সেল সীমানা অতিক্রম করছে যথেষ্ট নেন ভেরিয়েবলের মধ্যে (অর্থাত তুল্য এর পূর্ণসংখ্যা অংশ পরিবর্তন) tMaxX, tMaxYএবং tMaxZ। তারপরে, আমরা যেকোন অক্ষের সাথে কমপক্ষে - অর্থাৎ যেকোনো ভক্সেল-সীমানা নিকটে রয়েছে তার সাথে একটি পদক্ষেপ নিয়েছি ( stepএবং tDeltaভেরিয়েবলগুলি ব্যবহার করে ) tMax

/**
 * Call the callback with (x,y,z,value,face) of all blocks along the line
 * segment from point 'origin' in vector direction 'direction' of length
 * 'radius'. 'radius' may be infinite.
 * 
 * 'face' is the normal vector of the face of that block that was entered.
 * It should not be used after the callback returns.
 * 
 * If the callback returns a true value, the traversal will be stopped.
 */
function raycast(origin, direction, radius, callback) {
  // From "A Fast Voxel Traversal Algorithm for Ray Tracing"
  // by John Amanatides and Andrew Woo, 1987
  // <http://www.cse.yorku.ca/~amana/research/grid.pdf>
  // <http://citeseer.ist.psu.edu/viewdoc/summary?doi=10.1.1.42.3443>
  // Extensions to the described algorithm:
  //   • Imposed a distance limit.
  //   • The face passed through to reach the current cube is provided to
  //     the callback.

  // The foundation of this algorithm is a parameterized representation of
  // the provided ray,
  //                    origin + t * direction,
  // except that t is not actually stored; rather, at any given point in the
  // traversal, we keep track of the *greater* t values which we would have
  // if we took a step sufficient to cross a cube boundary along that axis
  // (i.e. change the integer part of the coordinate) in the variables
  // tMaxX, tMaxY, and tMaxZ.

  // Cube containing origin point.
  var x = Math.floor(origin[0]);
  var y = Math.floor(origin[1]);
  var z = Math.floor(origin[2]);
  // Break out direction vector.
  var dx = direction[0];
  var dy = direction[1];
  var dz = direction[2];
  // Direction to increment x,y,z when stepping.
  var stepX = signum(dx);
  var stepY = signum(dy);
  var stepZ = signum(dz);
  // See description above. The initial values depend on the fractional
  // part of the origin.
  var tMaxX = intbound(origin[0], dx);
  var tMaxY = intbound(origin[1], dy);
  var tMaxZ = intbound(origin[2], dz);
  // The change in t when taking a step (always positive).
  var tDeltaX = stepX/dx;
  var tDeltaY = stepY/dy;
  var tDeltaZ = stepZ/dz;
  // Buffer for reporting faces to the callback.
  var face = vec3.create();

  // Avoids an infinite loop.
  if (dx === 0 && dy === 0 && dz === 0)
    throw new RangeError("Raycast in zero direction!");

  // Rescale from units of 1 cube-edge to units of 'direction' so we can
  // compare with 't'.
  radius /= Math.sqrt(dx*dx+dy*dy+dz*dz);

  while (/* ray has not gone past bounds of world */
         (stepX > 0 ? x < wx : x >= 0) &&
         (stepY > 0 ? y < wy : y >= 0) &&
         (stepZ > 0 ? z < wz : z >= 0)) {

    // Invoke the callback, unless we are not *yet* within the bounds of the
    // world.
    if (!(x < 0 || y < 0 || z < 0 || x >= wx || y >= wy || z >= wz))
      if (callback(x, y, z, blocks[x*wy*wz + y*wz + z], face))
        break;

    // tMaxX stores the t-value at which we cross a cube boundary along the
    // X axis, and similarly for Y and Z. Therefore, choosing the least tMax
    // chooses the closest cube boundary. Only the first case of the four
    // has been commented in detail.
    if (tMaxX < tMaxY) {
      if (tMaxX < tMaxZ) {
        if (tMaxX > radius) break;
        // Update which cube we are now in.
        x += stepX;
        // Adjust tMaxX to the next X-oriented boundary crossing.
        tMaxX += tDeltaX;
        // Record the normal vector of the cube face we entered.
        face[0] = -stepX;
        face[1] = 0;
        face[2] = 0;
      } else {
        if (tMaxZ > radius) break;
        z += stepZ;
        tMaxZ += tDeltaZ;
        face[0] = 0;
        face[1] = 0;
        face[2] = -stepZ;
      }
    } else {
      if (tMaxY < tMaxZ) {
        if (tMaxY > radius) break;
        y += stepY;
        tMaxY += tDeltaY;
        face[0] = 0;
        face[1] = -stepY;
        face[2] = 0;
      } else {
        // Identical to the second case, repeated for simplicity in
        // the conditionals.
        if (tMaxZ > radius) break;
        z += stepZ;
        tMaxZ += tDeltaZ;
        face[0] = 0;
        face[1] = 0;
        face[2] = -stepZ;
      }
    }
  }
}

function intbound(s, ds) {
  // Find the smallest positive t such that s+t*ds is an integer.
  if (ds < 0) {
    return intbound(-s, -ds);
  } else {
    s = mod(s, 1);
    // problem is now s+t*ds = 1
    return (1-s)/ds;
  }
}

function signum(x) {
  return x > 0 ? 1 : x < 0 ? -1 : 0;
}

function mod(value, modulus) {
  return (value % modulus + modulus) % modulus;
}

গিটহাবের উত্সটির এই সংস্করণটির স্থায়ী লিঙ্ক


1
এই অ্যালগরিদমটিও নেতিবাচক সংখ্যার জায়গার জন্য কাজ করে? আমি কেবলমাত্র অ্যালগরিদম প্রয়োগ করেছি এবং সাধারণত আমি মুগ্ধ। এটি ইতিবাচক সমন্বয়কারীদের জন্য দুর্দান্ত কাজ করে। তবে কোনও কারণে নেতিবাচক স্থানাঙ্ক কখনও কখনও জড়িত থাকলে আমি অদ্ভুত ফলাফল পাই।
দানিজার

2
@danijar আমি নেতিবাচক স্থান এর সাথে কাজ করা intbounds / গেলিক ভাষার কাপড় পাই নি, তাই আমি এই ব্যবহার করুন: function intbounds(s,ds) { return (ds > 0? Math.ceil(s)-s: s-Math.floor(s)) / Math.abs(ds); }Infinityসমস্ত সংখ্যার চেয়ে বড় হিসাবে , আমি মনে করি না যে আপনি সেখানে 0 ডিএসি থেকে রক্ষা করতে হবে।
উইল

1
@ বটসকোনেট মনে হচ্ছে আপনার রশ্মি খুঁজে বের করার জন্য আপনার আনপোজিকেশন করার সমস্যা রয়েছে। আমার প্রথম দিকে এ জাতীয় সমস্যা ছিল। পরামর্শ: বিশ্বের স্থানটিতে উত্স থেকে উত্স + দিকনির্দেশে একটি রেখা আঁকুন। যদি সেই লাইনটি কার্সারের অধীনে না থাকে, বা যদি এটি কোনও বিন্দু হিসাবে উপস্থিত না হয় (যেহেতু প্রজেক্টড এক্স এবং ওয়াই সমান হওয়া উচিত) তবে আপনার আন-প্রজেক্টে সমস্যা রয়েছে ( এই উত্তরের কোডের অংশ নয় )। যদি এটি নির্ভরযোগ্যভাবে কার্সারের নীচে একটি বিন্দু হয় তবে সমস্যাটি রাইকাস্টে রয়েছে। আপনার যদি এখনও সমস্যা হয় তবে দয়া করে এই থ্রেডটি প্রসারিত করার পরিবর্তে একটি পৃথক প্রশ্ন জিজ্ঞাসা করুন।
কেভিন রেড

1
প্রান্তের কেসটি যেখানে রশ্মির উত্সের স্থানাঙ্ক একটি পূর্ণসংখ্যা মান এবং রশ্মির দিকের সংশ্লিষ্ট অংশটি negativeণাত্মক হয়। এই অক্ষটির জন্য প্রাথমিক টিম্যাক্স মানটি শূন্য হওয়া উচিত, যেহেতু উত্সটি ইতিমধ্যে তার ঘরের নীচে প্রান্তে রয়েছে, তবে এটি পরিবর্তে 1/dsঅন্য অক্ষগুলির মধ্যে একটিকে বাড়িয়ে তুলছে । intfloorউভয়টি dsনেতিবাচক এবং sএকটি পূর্ণসংখ্যা মান (মোড 0 প্রদান করে) হয় কিনা তা পরীক্ষা করে ঠিক করতে হয় এবং সেই ক্ষেত্রে 0.0 ফিরে আসে।
কোডওয়ারিয়র

2
এখানে আমার ityক্যের বন্দর: gist.github.com/dogfuntom/cc881c8fc86ad43d55d8 । যদিও, কিছু অতিরিক্ত পরিবর্তন সহ: ইন্টিগ্রেটেড উইল এবং কোডওয়ারিওরের অবদান এবং সীমাহীন বিশ্বে কাস্ট করা সম্ভব হয়েছিল।
ম্যাক্সিম কমলভ

1

সম্ভবত ব্রেনহ্যামের লাইন অ্যালগরিদমটি দেখুন , বিশেষত যদি আপনি ইউনিট-ব্লকগুলির সাথে কাজ করছেন (যেমন সর্বাধিক মাইনক্রাফ্টিশ গেমগুলি থাকে)।

মূলত এটি যে কোনও দুটি পয়েন্ট নেয় এবং তাদের মধ্যে একটি অখণ্ড রেখা চিহ্নিত করে। আপনি যদি প্লেয়ার থেকে তাদের সর্বাধিক বাছাইয়ের দূরত্বে কোনও ভেক্টর নিক্ষেপ করেন তবে আপনি এটি ব্যবহার করতে পারেন এবং প্লেয়ারদের পয়েন্ট হিসাবে অবস্থান রয়েছে।

অজগরটিতে আমার এখানে একটি 3D বাস্তবায়ন রয়েছে: bresenham3d.py


6
যদিও ব্রেনহ্যাম-ধরণের একটি অ্যালগোরিদম কিছু ব্লক মিস করবে। এটি প্রতিটি ব্লককে বিবেচনা করে না যে রে পাশ দিয়ে যায়; এটি এমন কিছু এড়িয়ে যাবে যাতে কিরণ ব্লক সেন্টারের কাছে পর্যাপ্ত পরিমাণে পায় না। আপনি উইকিপিডিয়ায় ডায়াগ্রাম থেকে এটি পরিষ্কার দেখতে পাচ্ছেন । উপরের-বাম দিকের কোণ থেকে তৃতীয় নিচে এবং তৃতীয় ডানদিকে ব্লকটি একটি উদাহরণ: লাইনটি এটি দিয়ে যায় (সবে) তবে ব্রেসেনহ্যামের অ্যালগরিদম এটি আঘাত করে না।
নাথান রেড

0

জন্য প্রথম ক্যামেরার সামনে ব্লক, লুপ জন্য একটি তৈরি যে কিছু সর্বাধিক দূরত্ব 0 থেকে লুপ। তারপরে, ক্যামেরার ফরোয়ার্ড ভেক্টরকে কাউন্টার দিয়ে গুণ করুন এবং পরীক্ষা করুন যে সেই অবস্থানের ব্লকটি শক্ত। যদি এটি হয়, তবে পরে ব্যবহারের জন্য ব্লকের অবস্থান সঞ্চয় করুন এবং লুপিং বন্ধ করুন।

আপনি যদি ব্লক স্থাপন করতে সক্ষম হতে চান তবে মুখোমুখি হওয়া আরও কঠিন নয়। কেবল ব্লক থেকে ফিরে লুপ করুন এবং প্রথম খালি ব্লকটি সন্ধান করুন।


কাজ করবে না, একটি কোণযুক্ত ফরোয়ার্ড ভেক্টর দিয়ে একটি ব্লকের একটি অংশের আগে পয়েন্ট এবং তারপরের পয়েন্টটি পরে, ব্লকটি অনুপস্থিত থাকা খুব সম্ভব হবে। এর সাথে একমাত্র সমাধান হ'ল বর্ধনের আকার হ্রাস করা, তবে অন্যান্য অ্যালগরিদমকে আরও কার্যকর করার জন্য আপনাকে এটিকে এত ছোট করতে হবে।
ফিল

এটি আমার ইঞ্জিনের সাথে বেশ ভাল কাজ করে; আমি 0.1 এর ব্যবধান ব্যবহার করি।
শিরোনামহীন

@ ফিল যেমন উল্লেখ করেছে, অ্যালগরিদমগুলি এমন ব্লকগুলি মিস করবে যেখানে কেবলমাত্র একটি ছোট প্রান্ত দেখা যায়। তবুও ব্লক স্থাপনের জন্য পিছনের দিকে লুপ করা কার্যকর হবে না। আমাদের পাশাপাশি লুপ করতে হবে এবং ফল একের পর এক হ্রাস করতে হবে।
দানিজার

0

আমি আমার প্রয়োগের সাথে রেডডিতে একটি পোস্ট করেছি , যা ব্রেসেনহ্যামের লাইন অ্যালগরিদম ব্যবহার করে। আপনি এটি কীভাবে ব্যবহার করবেন তার উদাহরণ এখানে:

// A plotter with 0, 0, 0 as the origin and blocks that are 1x1x1.
PlotCell3f plotter = new PlotCell3f(0, 0, 0, 1, 1, 1);
// From the center of the camera and its direction...
plotter.plot( camera.position, camera.direction, 100);
// Find the first non-air block
while ( plotter.next() ) {
   Vec3i v = plotter.get();
   Block b = map.getBlock(v);
   if (b != null && !b.isAir()) {
      plotter.end();
      // set selected block to v
   }
}

এখানে বাস্তবায়ন নিজেই:

public interface Plot<T> 
{
    public boolean next();
    public void reset();
    public void end();
    public T get();
}

public class PlotCell3f implements Plot<Vec3i>
{

    private final Vec3f size = new Vec3f();
    private final Vec3f off = new Vec3f();
    private final Vec3f pos = new Vec3f();
    private final Vec3f dir = new Vec3f();

    private final Vec3i index = new Vec3i();

    private final Vec3f delta = new Vec3f();
    private final Vec3i sign = new Vec3i();
    private final Vec3f max = new Vec3f();

    private int limit;
    private int plotted;

    public PlotCell3f(float offx, float offy, float offz, float width, float height, float depth)
    {
        off.set( offx, offy, offz );
        size.set( width, height, depth );
    }

    public void plot(Vec3f position, Vec3f direction, int cells) 
    {
        limit = cells;

        pos.set( position );
        dir.norm( direction );

        delta.set( size );
        delta.div( dir );

        sign.x = (dir.x > 0) ? 1 : (dir.x < 0 ? -1 : 0);
        sign.y = (dir.y > 0) ? 1 : (dir.y < 0 ? -1 : 0);
        sign.z = (dir.z > 0) ? 1 : (dir.z < 0 ? -1 : 0);

        reset();
    }

    @Override
    public boolean next() 
    {
        if (plotted++ > 0) 
        {
            float mx = sign.x * max.x;
            float my = sign.y * max.y;
            float mz = sign.z * max.z;

            if (mx < my && mx < mz) 
            {
                max.x += delta.x;
                index.x += sign.x;
            }
            else if (mz < my && mz < mx) 
            {
                max.z += delta.z;
                index.z += sign.z;
            }
            else 
            {
                max.y += delta.y;
                index.y += sign.y;
            }
        }
        return (plotted <= limit);
    }

    @Override
    public void reset() 
    {
        plotted = 0;

        index.x = (int)Math.floor((pos.x - off.x) / size.x);
        index.y = (int)Math.floor((pos.y - off.y) / size.y);
        index.z = (int)Math.floor((pos.z - off.z) / size.z);

        float ax = index.x * size.x + off.x;
        float ay = index.y * size.y + off.y;
        float az = index.z * size.z + off.z;

        max.x = (sign.x > 0) ? ax + size.x - pos.x : pos.x - ax;
        max.y = (sign.y > 0) ? ay + size.y - pos.y : pos.y - ay;
        max.z = (sign.z > 0) ? az + size.z - pos.z : pos.z - az;
        max.div( dir );
    }

    @Override
    public void end()
    {
        plotted = limit + 1;
    }

    @Override
    public Vec3i get() 
    {
        return index;
    }

    public Vec3f actual() {
        return new Vec3f(index.x * size.x + off.x,
                index.y * size.y + off.y,
                index.z * size.z + off.z);
    }

    public Vec3f size() {
        return size;
    }

    public void size(float w, float h, float d) {
        size.set(w, h, d);
    }

    public Vec3f offset() {
        return off;
    }

    public void offset(float x, float y, float z) {
        off.set(x, y, z);
    }

    public Vec3f position() {
        return pos;
    }

    public Vec3f direction() {
        return dir;
    }

    public Vec3i sign() {
        return sign;
    }

    public Vec3f delta() {
        return delta;
    }

    public Vec3f max() {
        return max;
    }

    public int limit() {
        return limit;
    }

    public int plotted() {
        return plotted;
    }



}

1
মন্তব্যে কেউ যেমন লক্ষ্য করেছেন, আপনার কোডটি অনথিভুক্ত। কোডটি সহায়ক হতে পারে, তবে এটি প্রশ্নের পুরোপুরি উত্তর দেয় না।
আনকো
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.