Computing normals to achieve flat and smooth shading

This article demonstrates in practice how to compute face and vertex normals to achieve flat and smooth shading using OpenGL.

Definitions

In geometry, a vector is an object which defines a direction and a norm. It is usually symbolized by an arrow pointing in the vector direction ; the length of the arrow giving the vector norm. When this norm is equal to 1 unit, the vector is normalized. A vector is normal to a surface when its direction is perpendicular to the plane which contains this surface:

In 3D computing, the normal vector of a surface defines the orientation of this surface in space. In particular, its orientation relative to light sources. As a result, OpenGL uses this vector to determine how much light each point of a given surface receives. Failing to give this vector alongside the surface definition itself make OpenGL renders a uniformly lighten object (no shading). When one normal is given alongside the surface, OpenGL enlighten all the surface points with the same color value. The result is much more realistic than without any shading and is known as flat shading rendering. To realize an even more aesthetic enlightenment, you must give to OpenGL, one normal vector per surface vertex. The result is good looking and is known as smooth shading rendering. Look at the figure below how each of these methods renders:

The sample project

You can download below the sample MFC project we used to illustrate this article. It has been created with Visual C++ 5 and tested on Windows NT4. This sample take a static object definition to draw through OpenGL the twisted torus you can see in the figure above. This object is defined by an array of triangular faces, each of them pointing in turn into an array of vertices. Hereafter is an excerpt of these definitions:


// Static definition of the object's vertices
struct GLpoint {
   GLfloat x, y, z;
} OBJ_VERTICES [] = {

   {(float)54.111641, (float)-0.007899, (float)37.141083}, 
   {(float)55.552414, (float)-5.571973, (float)41.828125}, 

   // ... //

   {(float)49.429958, (float)5.559381, (float)35.695301}, 
   {(float)54.111732, (float)-0.007808, (float)37.141174}
};


// Static definition of the object's faces
struct GLFace {
   unsigned short v1, v2, v3;
} OBJ_FACES[] = {

   {0, 11, 12}, 
   {0, 12, 1},
 
   // ... //

   {878, 9, 0}, 
   {878, 0, 869}
};

Computing face normals

To achieve flat shading in our example, each face normal is computed using the ComputeFaceNormal() function at the end of the listing below. Using the three vertices of the face, the function constructs two vectors a and b, which share the middle vextex p2 at their origin. Those vectors are transmitted to the VectorGetNormal() function which uses the determinant method to cross product the two arguments. The resulting vector is normal to the surface defined by a and b. The last step consists to normalize it to avoid additional computation in OpenGL.

// Offset pIn by pOffset into pOut
void VectorOffset (GLpoint *pIn, GLpoint *pOffset, GLpoint *pOut)
{
   pOut->x = pIn->x - pOffset->x;
   pOut->y = pIn->y - pOffset->y;
   pOut->z = pIn->z - pOffset->z;
}

// Compute the cross product a X b into pOut
void VectorGetNormal (GLpoint *a, GLpoint *b, GLpoint *pOut)
{
   pOut->x = a->y * b->z - a->z * b->y;
   pOut->y = a->z * b->x - a->x * b->z;
   pOut->z = a->x * b->y - a->y * b->x;
}

// Normalize pIn vector into pOut
bool VectorNormalize (GLpoint *pIn, GLpoint *pOut)
{
   GLfloat len = (GLfloat)(sqrt(sqr(pIn->x) + sqr(pIn->y) + sqr(pIn->z)));
   if (len)
   {
      pOut->x = pIn->x / len;
      pOut->y = pIn->y / len;
      pOut->z = pIn->z / len;
      return true;
   }
   return false;
}

// Compute p1,p2,p3 face normal into pOut
bool ComputeFaceNormal (GLpoint *p1, GLpoint *p2, GLpoint *p3, GLpoint *pOut)
{
   // Uses p2 as a new origin for p1,p3
   GLpoint a;
   VectorOffset(p3, p2, &a);
   GLpoint b;
   VectorOffset(p1, p2, &b);
   // Compute the cross product a X b to get the face normal
   GLpoint pn;
   VectorGetNormal(&a, &b, &pn);
   // Return a normalized vector
   return VectorNormalize(&pn, pOut);
}

Computing vertex normals

Computing each vertex normal is straightforward when you already have all face normals. Take a look at the ComputeVerticeNormal() function below:


void GLSObject::ComputeVerticeNormal (int ixVertice)
{
   // Allocate a temporary storage to store adjacent faces indexes
   if (!m_pStorage)
   {
      m_pStorage = new int[m_nbFaces];
      if (!m_pStorage)
         return;
   }

   // Store each face which has an intersection with the ixVertice'th vertex
   int nbAdjFaces = 0;
   GLFace * pFace = (GLFace *)&OBJ_FACES;
   for (int ix = 0; ix < m_nbFaces; ix++, pFace++)
      if (pFace->v1 == ixVertice)
         m_pStorage[nbAdjFaces++] = ix;
      else
         if (pFace->v2 == ixVertice)
            m_pStorage[nbAdjFaces++] = ix;
         else
            if (pFace->v3 == ixVertice)
               m_pStorage[nbAdjFaces++] = ix;

   // Average all adjacent faces normals to get the vertex normal
   GLpoint pn;
   pn.x = pn.y = pn.z = 0;
   for (int jx = 0; jx < nbAdjFaces; jx++) 
   { 
      int ixFace= m_pStorage[jx];
      pn.x += m_pFaceNormals[ixFace].x;
      pn.y += m_pFaceNormals[ixFace].y;
      pn.z += m_pFaceNormals[ixFace].z;
   } 
   pn.x /= nbAdjFaces;
   pn.y /= nbAdjFaces; 
   pn.z /= nbAdjFaces; 
   
   // Normalize the vertex normal 
   VectorNormalize(&pn, &m_pVertNormals[ixVertice]); 
}

To compute the normal at vertex ixVertice, this function search all faces which share the vertex. The normal of all those adjacent faces are then averaged to get the vertex normal, as shown on the following picture:

The result is finally normalized into a previously allocated array of vectors; a member of this.

Exploitation with OpenGL

The current normal vector is set by calling glNormal3*(). OpenGL assigns this normal to any subsequent vertex definition invoked by glVertex3*(). To achieve flat shading, the principle is to call glNormal3*() before the first glVextex3*() call for the surface. For smooth shading, each call to glVextex3*() must be preceded by a corresponding call to glNormal3*():


void GLSObject::Draw (WORD wFlags)
{
   // ... //

   // Draw mesh
   glBegin(GL_TRIANGLES);
   GLFace * pFace = (GLFace *)&OBJ_FACES;
   for (int ix = 0; ix < m_nbFaces; ix++, pFace++)
   { 
      if (m_pFaceNormals)
         if (wFlags & DF_FLAT)
            // Flat shading 
            glNormal3fv((float *)&m_pFaceNormals[ix]); 
         else 
            if (m_pVertNormals && (wFlags & DF_SMOOTH)) 
               // Smooth shading
               glNormal3fv((float *)&m_pVertNormals[pFace->v1]);

      glVertex3fv((float *)&OBJ_VERTICES[pFace->v1]);

      if (m_pVertNormals && (wFlags & DF_SMOOTH))
         // Smooth shading
         glNormal3fv((float *)&m_pVertNormals[pFace->v2]);

      glVertex3fv((float *)&OBJ_VERTICES[pFace->v2]);

      if (m_pVertNormals && (wFlags & DF_SMOOTH))
         // Smooth shading
         glNormal3fv((float *)&m_pVertNormals[pFace->v3]);

      glVertex3fv((float *)&OBJ_VERTICES[pFace->v3]);	
   }
   glEnd();


   // ... //
}

Download demo project - 65 KB



Comments

  • Remise ghd pink 2010 vente chaude Oslo

    Posted by pletchercpa on 07/15/2013 11:29pm

    Les images sur les sites Web qui vendent ces lisseur GHD fausses ne sont pas toujours ce que vous obtenez en fait. Parfois, le site contient des images de vrais lisseur GHD, alors que vous vous retrouvez avec un imitation.Because pas cher, il ya tant de sites Web qui vendent lisseur GHD bon marché, il peut être difficile de dire quels sont ceux qui sont légitimes. Et il peut souvent être encore plus difficile de dire si redresseurs qui est vrai lorsque vous les avez reçus. [url=http://ghdpascherfer.tripod.com/]ghd pas cher[/url] Un magazine InStyle Best Buy de beauté depuis plusieurs années running.Hai sont des plaques en céramique sont recouvertes pour protéger les cheveux de la chaleur. Ce fer plat devient assez chaude pour défriser les cheveux (et des cheveux reste droit), mais pas trop chaud pour endommager vos cheveux. De plus, il est prêt à utiliser en moins de 30 secondes. Peut-être le meilleur fer plat sur le marché aujourd'hui, la piste de chaleur FHI est un favori parmi les pros, car il crée élégant, cheveux lisses avec seulement une ou deux passes, selon styliste Jonathan Hanousek dans le numéro 2009 de InStyle Magazine, qui a nommé un Meilleur beauté Acheter en 2009. Avec une étiquette de prix de plus de 300 $, la piste de FHI Heat (avec plaques de tourmaline, bien sûr) durent toute une vie. [url=http://ghdpascherfer.tripod.com/]ghd lisseur[/url] En raison de la popularité de masse et le prix du lisseur GHD, ils sont devenus une opportunité lucrative pour les fabricants de produits de contrefaçon, avec lunettes de soleil, bottes Ugg et le succès de toute entreprise sportswear.The conduit inévitablement à la production de bon marché de contrefaçons et lisseurs GHD est pas de sociétés exception.The qui mettent en place des sites Web GHD fausses sont généralement basées sur l'Extrême-Orient qui est aussi celle où elles produisent leurs produits. Ils achètent ensuite. com and.co.uk noms de site Web à regarder de plus légitime. Vous devez être prudent, juste parce qu'un site se termine po co.uk, cela ne signifie pas que les produits proviennent du Royaume-Uni. La personne qui gère le site peut être n'importe où dans le monde.

    Reply
  • Por qué planchas ghd son tan populares superventas ghd en España

    Posted by pletcherfxs on 06/13/2013 11:48pm

    [url=http://www.planchaghdbaratasofertas.es/category/planchas-de-pelo-ghd]planchas de pelo ghd[/url] Pocos meses atrás, cuando yo estaba buscando un programador de máquina de ritmo, tuve la suerte de tropezar con una aplicación para PC que me dejó impresionado y aún hoy, estoy seguro de que no hay nada que pueda igualarlo.Este programa se llama Dub baratos alisadores de pelo ghd Turbo, y sospecho que es el último kit para hacer sus propios ritmos. No puedo creer que usted puede conseguir un teclado totalmente funcional con la octava, y caja de ritmos, secuenciador de la pista, premade ritmos y mucho más. [url=http://www.planchaghdbaratasofertas.es/category/nueva-plancha-ghd]planchas ghd ofertas[/url] Gracias a Twitter, he descubierto que algunos de ustedes estaban dispuestos a descubrir cuál es la diferencia entre el Cloud Nine y el ghd IV Styler fueron – y si valía la pena cambiar de lealtades o actualizar desde una alternativa más barata. Así que he creado esta tabla de comparación rápida que espero que le ayuda a tomar una decisión.Las mujeres han luchado por conseguir la perfección salón de golpe el cabello secado en su casa durante años. Mi cabello es grueso y recto, naturalmente, así que he estado en la búsqueda de un secador de pelo increíble que es bastante, pero se seca el pelo rápidamente y me da un poco de volumen. [url=http://www.baratasplanchas.org/]ghd baratas[/url] Estos defienden el cabello contra el daño por calor, con una armadura invisible – perfecto para su uso con una plancha GHD. El paso de estilos utiliza activados por el calor aerosoles para el cabello, bálsamos, lociones y cremas para crear estilos exquisitos en el cabello saludable ahora protegidos.El paso final final usa aerosoles, ceras y sueros para la gestión duradera espera, la textura y el brillo. Utilizando la gama de productos de cuatro pasos junto con la plancha de pelo GHD usted será capaz de crear confianza hermoso, sano, recto y sin encrespar el cabello. Usted puede incluso crear rizos deliciosos.

    Reply
  • Trist surrounded to be qualified to variation power attached to customers

    Posted by carpinteyrorcc on 06/13/2013 09:43am

    [url=http://monsterbeats.australiacheapbeats.com.au/]Beats by dre australia[/url] The Beats Dr Dre Headphones be undergoing suit more and more commonplace in mordern world. Artists and producers waste countless hours fine-tuning and mixing music to take home it strictly how they pine for their fans to perceive it. But the vast majority of headphones can¡¯t accurately reproduce the intricacies produced in the studio. Totally lob,Studios can. With precision-engineered, advanced demagogue invent, powered amplification, and powered noise stoppage, you discover music the feature today¡¯s zenith artists and producers stand in want you to hear. High Definition On-Ear Headphones fashion fashion of temerarious alteration, lambent improper lines add to each other and the neighbourhood environment. strife with noiselessness in the basics, Monster beats headphone hope Beats before dr. dre to apprehend some unlike wildlife. [url=http://shopoutlet.australiacheapbeats.com.au/]Beats by dre australia[/url] You may also like this Missing link Turbine Earphones, Looking for the pubescent users, hipop fans and NBA fans, the outward appearance is also of the standards looking for a Monster Beats Dr Dre, and even a central standard. As a brand-new emerging force in headphone effort, Eyesore products melody counterpart distress on both publication and acoustic. It is not a odd shit to accept that Ogre Beats Studiobecomes the pet determination as a remedy for lots of stars. [url=http://beatsbydre.australiacheapbeats.com.au/]beats by dre headphones[/url] The music, we monster beats recall this together dexterity as far as something thousands of years seems to require unbreakable cords with humans; it brings more artfulness on the shock Qiexi, carnival.When you sup, when accompanied close to harmonious music and cleverness to yield b reveal the mood, you resolution handle fashion come up against, energetic and cordial feeling.Buy cheap Beats by means of Dre, ways to blot phony or earnest Monstrosity Beats Studio.It desire make over you some various feelings.People make like this feeling.

    Reply
  • GHD rettetang er en av verdens mest populære merkene

    Posted by wanzilucky on 06/07/2013 06:15pm

    [url=http://bestghdrettetangtilbud.blogspace.fr/]ghd rettetang[/url] Som vi alle vet, er ghd straighteners nå et kjent navn. men det er fortsatt klager mange mennesker om sine høye priser, dette også føre til mange mennesker kan ikke eie ghd straighteners. Faktisk kan du kjøpe en billig ghd straightener.ghd rettetang Billig er ønsket velkommen av folk. De også kan møte dine behov. I 2001, er ghd rettetang billig forpliktet til å utvikle den mest effektive og innovative produkter til hårpleie bransjen. GHD creats annerledes og vakkert ghd hår straighteners fører til fantastiske resultater. [url=http://www.rettetangnorgenews.net/beste-ghd-rettetang/beste-ghd-rettetang.html]beste ghd rettetang[/url] Julegavene pakkes inn i fargerikt julegavepapir, merkes med navnelapper og fordeles før julaften. Pakkene åpnes vanligvis hjemme, i Skandinavia som regel under familiefesten på julaften, i Storbritannia og USA første juledag. I norske småbarnsfamilier blir mange av gavene delt ut av en utkledd julenisse som kommer med dem i sekken sin. Ellers er det vanligst å samle julegavene i en haug under juletreet og åpne dem når familien er samlet.Trodde julen når du sender GHD? [url=http://bestghdrettetangtilbud.blogspace.fr/]ghd rettetang[/url] Passer perfekt til de spesielle behovene til farget hår. GHD rettetang for farget hår beskytter ditt ømfintlige fargede hår fra å tørke ut mens den hindrer fargen i å falme med opptil 70 %*. Du kan innstille denne spesielle varmejusteringen etter som håret ditt er tynt, normalt eller tykt, mens den fleksible platen minimerer for mye trykk. Resultatet er vakre, elegante hårfrisyrer med minimalt med falming av fargen.

    Reply
  • FÃ¥ de bedste hovedtelefoner monster beats i

    Posted by wanzixiao on 06/03/2013 06:22pm

    [url=http://www.beatsbydrehretelefoner.weebly.com/]Beats by dre billig pris[/url] Som en atlet de ‘træning, som’, jeg gør, hvad jeg skal gøre for at blive klar “kan hovedtelefonen hjælper mig til at føle jeg er så meget tættere på min musik, der hjælper mig udføre, hjælper mig med at finde ud af, om jeg vil at være intens? Uanset hvad jeg ønsker at være på banen den dag, musik hjælper mig derhen. Da de sagde ja, vil produktet blive valgt og hjælpe dem med at opretholde den bedste tilstand I spillet.Ifølge en talsmand for selskabet, ved Dre endnu ikke har haft en dialog med IOC om “ambush marketing” praksis af atleter iført og twitte om de sanktioneret hovedtelefoner Beats. Men Omar Johnson, Beats senior VP for markedsføring, fortæller Billboard.biz at virksomheden arbejder med atleter året rundt. [url=http://www.nyebeatsbydrdre.350.com/]Nye beats by dr dre[/url] Beats by Dr. Dre Studio – hvor det faktisk fungere særdeles godt på alle enhederne hver især.Vi vil alle sammen have mest muligt udbytte af headsettet og lydkvaliteten, når vi snakker om musik. Musikken skal glide igennem øregangen og det skal lyde formidabelt foruden kunstnerens eget værk. Derudover hævdes der jo også at man mister hørelsen med al for høj lyd, men i disse Beats er det overhovedet ikke en nødvendighed at skrue særlig højt op, da de har elektronisk støjdæmning, som holder støjen på ydersiden og musikken på indersiden og dermed er lyden klar og går her direkte glidende igennem. Selve høretelefonerne sidder utrolig godt på hovedet, da der er bløde puder på henholdsvis bøjlen og ørekopperne, hvor de også omslutter sig ørerne rigtig godt. Funktionerne på høretelefonerne er ganske lige til, da der sidder en tænd/sluk-knap, som gør at den indbyggede forstærkerkreds fungere. Ligeledes kan man holde logoet (b’et) inde på højre, som gør det muligt [url=http://www.beatsbydrdredanmark.webgarden.com/]Beats by dre billig pris[/url] BIII booo din troede involverer let jazz, med essentials involverer normal, god ole ‘, sammen med digitalkameraer sange for dig at lave en ny specifikt eksklusiv lyd. BIII øjeblikket omdefinerer private musik ekspertise til at gøre dine musikernes øretelefoner. Enhver observere, hver eneste nuance, kan optaget med aldrig-hørt-før realistisk look, plus iøjnefaldende design og stil trådløse høretelefoner bestemt ved en ny trompet mundstykke har som en installation vidnesbyrd om den mestre kunsten. Disse former for sædvanligvis er ikke kun de bedste ørepuder vedrørende jazz. Uanset hvad nogen hører, vil BIII sikkert vokset til betragtes som en af dine nuværende all-time musikalske teknologi højder.

    Reply
  • kategorier:chi fladjern

    Posted by motherdhmm on 05/30/2013 02:46am

    [url=http://www.buy-beatsdrdre.com/category/new-beats-dr-dre-headphones]Beats By Dr Dre[/url] Der er GHD IV styler straightenrs på internettet til handel, lave en vurdering af stræk fleece. Derfor skal vi gøres manuelt, men det er bare for at holde godt for cusomers.ghd glattejern. [url=http://www.buy-beatsdrdre.com/]beats by dre australia[/url] Den mest kendte er ghd IV Salon Styler som bliver brugt verden over på blandt andet modeshows og catwalk. Fordelen ved en IV Salon Styler er de brede plader. Det betyder at man kan glatte større områder af gangen og dermed bliver hurtigere færdige. Ulempen er at de brede plader f.eks. ikke er optimale til at style håret. Vi du både kunne glatte dit hår og samtidig bruge dit ghd glattejern til at lave f.eks. krøller, så er ghd IV styler et oplagt valg. Den kan det hele og glatter også dit hår perfekt. Men den er lidt langsommere end hvis man vælger den brede salon styler fra ghd. Endelig er der også den helt smalle ghd IV Mini Styler. [url=http://www.buy-beatsdrdre.com/]beats by dre[/url] Få et glattejern som din frisør også ville bruge. De fleste frisør saloner har brug for unedbrydelige glattejern som ghd fladjern tilbud holder til at blive brugt hver eneste dag. Derfor har hoveddelen af saloner verden over valgt at benytte GHD glattejernene.

    Reply
  • Perfectionism.

    Posted by Syhon on 04/19/2010 03:48am

    I'm very late in replying to this, clearly; however, I would like to note simply that "vertex" is singular and "vertices" is plural. The code has "vertice" written throughout and this is entirely incorrect.

    Reply
  • Did you print out vertex normals?

    Posted by Legacy on 08/10/2000 12:00am

    Originally posted by: Jae Hun, Ryu

    This program is working well. When I printed out vertex
    normal, however, some vertex normal has infinite value.
    Would you check the function of calculating vertex normals?
    I also try to debug it.

    FILE *fp=fopen(szFileName,"w");
    fprintf(fp,"Vertex Normals\n");
    for (int i=0 ; i< this->m_nbVertices; i++){
    float x,y,z;
    x=m_pVertNormals[i].x;
    y=m_pVertNormals[i].y;
    z=m_pVertNormals[i].z;
    fprintf(fp, "%f %f %f\n", x,y,z);
    }
    fclose(fp);

    --------- Output--------------------
    0.706771 -0.005534 -0.707421
    0.827826 -0.465777 -0.312659
    0.633619 -0.747251 0.200356
    0.197954 -0.745792 0.636089
    -0.313860 -0.461939 0.829521
    -0.706423 -0.002511 0.707785
    -0.829539 0.459008 0.318082
    -0.635861 0.747466 -0.192290
    -0.198640 0.749445 -0.631565
    0.314909 0.460325 -0.830020
    -431602080.000000 -431602080.000000 -431602080.000000
    0.698595 -0.168453 -0.695405
    ............................

    Reply
  • more cpu but better result

    Posted by Legacy on 11/24/1999 12:00am

    Originally posted by: Fonzy

    I have yet made the same algorithm to calculate smoot but the result is uggly for object that have small faces next to big one.

    Another way is to multiply the adjacent face normal with the inverse of distance from the vertex (to the center of the adjacent face). Then you do a average and you normalize the result.

    ps : i can not give the source because of copyright.

    see samples at http://ferretti.homepage.com (current project)

    Reply
  • About the efficiency of Computing normals

    Posted by Legacy on 06/04/1999 12:00am

    Originally posted by: EnPengXu


    I think the below method can improve some efficiency.

    void GLSObject::ComputeNormal()
    {
    GLFace * pFace = (GLFace *)&OBJ_FACES;
    GLpoint Normal ;
    memset( m_pStorage , 0 , sizeof(int)*m_nbVertex );
    memset( m_pVertNormals , 0 , sizeof( GLpoint ) * m_nbVertex );
    // Normalize the vertex normal
    for( int i = 0 ; i < m_nbFaces ; i ++ )
    {
    int v1 = pFace[i].v1 ;
    int v2 = pFace[i].v2 ;
    int v3 = pFace[i].v3 ;

    ComputeFaceNormal( OBJ_VERTICES + v1 ,
    OBJ_VERTICES + v2 ,
    OBJ_VERTICES + v3 ,
    &Normal );
    // Now Normal has been normalized
    // GLpoint::operator + fuction should be defined
    m_pVertNormals[v1] += Nromal ;
    m_pVertNormals[v2] += Nromal ;
    m_pVertNormals[v3] += Nromal ;
    }
    for( i = 0 ; i < m_nbVertex ; i ++ )
    VectorNormalize( m_pVertNormals+i, m_pVertNormals+i);
    }

    But in most case , you want to get more 'smooth' effection , you should define three normals for each face, that's say , a vertex should has more than one normals that
    rest on the face numbers it connected. when you computing the normals , you should comput the angles between two faces and compare it to a little value to determine whether
    should average the two triangle's normal or not .

    Reply
  • Loading, Please Wait ...

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Protecting business operations means shifting the priorities around availability from disaster recovery to business continuity. Enterprises are shifting their focus from recovery from a disaster to preventing the disaster in the first place. With this change in mindset, disaster recovery is no longer the first line of defense; the organizations with a smarter business continuity practice are less impacted when disasters strike. This SmartSelect will provide insight to help guide your enterprise toward better …

  • Corporate e-Learning technology has a long and diverse pedigree. As far back as the 1980s, companies were adopting computer-based training to supplement traditional classroom activities. More recently, rich web-based applications have added streaming audio and video, real-time collaboration and other new tools to the e-Learning mix. At the same time, the growing availability of informal learning tools--a category that includes everything from web searches to social media posts--are having a major impact on …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds